From 7822091a5f536edb315f658979af04043ad609e8 Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Wed, 30 Mar 2022 15:15:34 +0200 Subject: Move f/o/n/ethernet to p/m/Connectivity Test: TH Bug: 222234190 Change-Id: Iea43a76d84002a5a587fa735b692d8b6ce6fd12e --- Android.bp | 32 - TEST_MAPPING | 12 - .../server/ethernet/EthernetConfigStore.java | 88 -- .../server/ethernet/EthernetNetworkAgent.java | 65 -- .../server/ethernet/EthernetNetworkFactory.java | 785 ----------------- .../android/server/ethernet/EthernetService.java | 47 - .../server/ethernet/EthernetServiceImpl.java | 299 ------- .../android/server/ethernet/EthernetTracker.java | 943 --------------------- tests/Android.bp | 53 -- tests/AndroidManifest.xml | 28 - .../ethernet/EthernetNetworkFactoryTest.java | 783 ----------------- .../server/ethernet/EthernetServiceImplTest.java | 372 -------- .../server/ethernet/EthernetTrackerTest.java | 456 ---------- 13 files changed, 3963 deletions(-) delete mode 100644 Android.bp delete mode 100644 TEST_MAPPING delete mode 100644 java/com/android/server/ethernet/EthernetConfigStore.java delete mode 100644 java/com/android/server/ethernet/EthernetNetworkAgent.java delete mode 100644 java/com/android/server/ethernet/EthernetNetworkFactory.java delete mode 100644 java/com/android/server/ethernet/EthernetService.java delete mode 100644 java/com/android/server/ethernet/EthernetServiceImpl.java delete mode 100644 java/com/android/server/ethernet/EthernetTracker.java delete mode 100644 tests/Android.bp delete mode 100644 tests/AndroidManifest.xml delete mode 100644 tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java delete mode 100644 tests/java/com/android/server/ethernet/EthernetServiceImplTest.java delete mode 100644 tests/java/com/android/server/ethernet/EthernetTrackerTest.java diff --git a/Android.bp b/Android.bp deleted file mode 100644 index c429a6f..0000000 --- a/Android.bp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2014 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. - -// Build the java code -// ============================================================ - -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -filegroup { - name: "ethernet-service-updatable-sources", - srcs: [ - "java/**/*.java", - ], - path: "java", - visibility: [ - "//frameworks/opt/net/ethernet/tests", - "//packages/modules/Connectivity/service-t", - ], -} diff --git a/TEST_MAPPING b/TEST_MAPPING deleted file mode 100644 index 24c152b..0000000 --- a/TEST_MAPPING +++ /dev/null @@ -1,12 +0,0 @@ -{ - "presubmit": [ - { - "name": "EthernetServiceTests" - } - ], - "imports": [ - { - "path": "packages/modules/Connectivity" - } - ] -} diff --git a/java/com/android/server/ethernet/EthernetConfigStore.java b/java/com/android/server/ethernet/EthernetConfigStore.java deleted file mode 100644 index 6b623f4..0000000 --- a/java/com/android/server/ethernet/EthernetConfigStore.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2014 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.net.IpConfiguration; -import android.os.Environment; -import android.util.ArrayMap; - -import com.android.server.net.IpConfigStore; - - -/** - * This class provides an API to store and manage Ethernet network configuration. - */ -public class EthernetConfigStore { - private static final String ipConfigFile = Environment.getDataDirectory() + - "/misc/ethernet/ipconfig.txt"; - - private IpConfigStore mStore = new IpConfigStore(); - private ArrayMap mIpConfigurations; - private IpConfiguration mIpConfigurationForDefaultInterface; - private final Object mSync = new Object(); - - public EthernetConfigStore() { - mIpConfigurations = new ArrayMap<>(0); - } - - public void read() { - synchronized (mSync) { - ArrayMap configs = - IpConfigStore.readIpConfigurations(ipConfigFile); - - // This configuration may exist in old file versions when there was only a single active - // Ethernet interface. - if (configs.containsKey("0")) { - mIpConfigurationForDefaultInterface = configs.remove("0"); - } - - mIpConfigurations = configs; - } - } - - 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); - } - - if (modified) { - mStore.writeIpConfigurations(ipConfigFile, mIpConfigurations); - } - } - } - - public ArrayMap getIpConfigurations() { - synchronized (mSync) { - return new ArrayMap<>(mIpConfigurations); - } - } - - @Nullable - public IpConfiguration getIpConfigurationForDefaultInterface() { - synchronized (mSync) { - return mIpConfigurationForDefaultInterface == null - ? null : new IpConfiguration(mIpConfigurationForDefaultInterface); - } - } -} diff --git a/java/com/android/server/ethernet/EthernetNetworkAgent.java b/java/com/android/server/ethernet/EthernetNetworkAgent.java deleted file mode 100644 index 57fbce7..0000000 --- a/java/com/android/server/ethernet/EthernetNetworkAgent.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2021 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.content.Context; -import android.net.LinkProperties; -import android.net.NetworkAgent; -import android.net.NetworkAgentConfig; -import android.net.NetworkCapabilities; -import android.net.NetworkProvider; -import android.net.NetworkScore; -import android.os.Looper; -import android.annotation.NonNull; -import android.annotation.Nullable; - -public class EthernetNetworkAgent extends NetworkAgent { - - private static final String TAG = "EthernetNetworkAgent"; - - public interface Callbacks { - void onNetworkUnwanted(); - } - - private final Callbacks mCallbacks; - - EthernetNetworkAgent( - @NonNull Context context, - @NonNull Looper looper, - @NonNull NetworkCapabilities nc, - @NonNull LinkProperties lp, - @NonNull NetworkAgentConfig config, - @Nullable NetworkProvider provider, - @NonNull Callbacks cb) { - super(context, looper, TAG, nc, lp, new NetworkScore.Builder().build(), config, provider); - mCallbacks = cb; - } - - @Override - public void onNetworkUnwanted() { - mCallbacks.onNetworkUnwanted(); - } - - // sendLinkProperties is final in NetworkAgent, so it cannot be mocked. - public void sendLinkPropertiesImpl(LinkProperties lp) { - sendLinkProperties(lp); - } - - public Callbacks getCallbacks() { - return mCallbacks; - } -} diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java deleted file mode 100644 index d910629..0000000 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Copyright (C) 2014 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.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.ConnectivityResources; -import android.net.EthernetManager; -import android.net.EthernetNetworkSpecifier; -import android.net.EthernetNetworkManagementException; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.IpConfiguration; -import android.net.IpConfiguration.IpAssignment; -import android.net.IpConfiguration.ProxySettings; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkAgentConfig; -import android.net.NetworkCapabilities; -import android.net.NetworkFactory; -import android.net.NetworkProvider; -import android.net.NetworkRequest; -import android.net.NetworkSpecifier; -import android.net.ip.IIpClient; -import android.net.ip.IpClientCallbacks; -import android.net.ip.IpClientManager; -import android.net.ip.IpClientUtil; -import android.net.shared.ProvisioningConfiguration; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.Looper; -import android.os.RemoteException; -import android.text.TextUtils; -import android.util.AndroidRuntimeException; -import android.util.Log; -import android.util.SparseArray; - -import com.android.connectivity.resources.R; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.net.module.util.InterfaceParams; - -import java.io.FileDescriptor; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -/** - * {@link NetworkFactory} that represents Ethernet networks. - * - * 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. - */ -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 LEGACY_TCP_BUFFER_SIZES = - "524288,1048576,3145728,524288,1048576,2097152"; - - private final ConcurrentHashMap mTrackingInterfaces = - new ConcurrentHashMap<>(); - private final Handler mHandler; - private final Context mContext; - final Dependencies mDeps; - - public static class Dependencies { - public void makeIpClient(Context context, String iface, IpClientCallbacks callbacks) { - IpClientUtil.makeIpClient(context, iface, callbacks); - } - - public IpClientManager makeIpClientManager(@NonNull final IIpClient ipClient) { - return new IpClientManager(ipClient, TAG); - } - - public EthernetNetworkAgent makeEthernetNetworkAgent(Context context, Looper looper, - NetworkCapabilities nc, LinkProperties lp, NetworkAgentConfig config, - NetworkProvider provider, EthernetNetworkAgent.Callbacks cb) { - return new EthernetNetworkAgent(context, looper, nc, lp, config, provider, cb); - } - - public InterfaceParams getNetworkInterfaceByName(String name) { - return InterfaceParams.getByName(name); - } - - // TODO: remove legacy resource fallback after migrating its overlays. - private String getPlatformTcpBufferSizes(Context context) { - final Resources r = context.getResources(); - final int resId = r.getIdentifier("config_ethernet_tcp_buffers", "string", - context.getPackageName()); - return r.getString(resId); - } - - public String getTcpBufferSizesFromResource(Context context) { - final String tcpBufferSizes; - final String platformTcpBufferSizes = getPlatformTcpBufferSizes(context); - if (!LEGACY_TCP_BUFFER_SIZES.equals(platformTcpBufferSizes)) { - // Platform resource is not the historical default: use the overlay. - tcpBufferSizes = platformTcpBufferSizes; - } else { - final ConnectivityResources resources = new ConnectivityResources(context); - tcpBufferSizes = resources.get().getString(R.string.config_ethernet_tcp_buffers); - } - return tcpBufferSizes; - } - } - - public static class ConfigurationException extends AndroidRuntimeException { - public ConfigurationException(String msg) { - super(msg); - } - } - - public EthernetNetworkFactory(Handler handler, Context context) { - this(handler, context, new Dependencies()); - } - - @VisibleForTesting - EthernetNetworkFactory(Handler handler, Context context, Dependencies deps) { - super(handler.getLooper(), context, NETWORK_TYPE, createDefaultNetworkCapabilities()); - - mHandler = handler; - mContext = context; - mDeps = deps; - - setScoreFilter(NETWORK_SCORE); - } - - @Override - public boolean acceptRequest(NetworkRequest request) { - if (DBG) { - Log.d(TAG, "acceptRequest, request: " + request); - } - - return networkForRequest(request) != null; - } - - @Override - protected void needNetworkFor(NetworkRequest networkRequest) { - 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(); - } - } - - @Override - protected void releaseNetworkFor(NetworkRequest networkRequest) { - NetworkInterfaceState network = networkForRequest(networkRequest); - if (network == null) { - Log.e(TAG, "releaseNetworkFor, failed to get a network for " + networkRequest); - return; - } - - if (--network.refCount == 0) { - network.stop(); - } - } - - /** - * Returns an array of available interface names. The array is sorted: unrestricted interfaces - * goes first, then sorted by name. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected String[] getAvailableInterfaces(boolean includeRestricted) { - return mTrackingInterfaces.values() - .stream() - .filter(iface -> !iface.isRestricted() || includeRestricted) - .sorted((iface1, iface2) -> { - int r = Boolean.compare(iface1.isRestricted(), iface2.isRestricted()); - return r == 0 ? iface1.name.compareTo(iface2.name) : r; - }) - .map(iface -> iface.name) - .toArray(String[]::new); - } - - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected void addInterface(@NonNull final String ifaceName, @NonNull final String hwAddress, - @NonNull final IpConfiguration ipConfig, - @NonNull final NetworkCapabilities capabilities) { - if (mTrackingInterfaces.containsKey(ifaceName)) { - Log.e(TAG, "Interface with name " + ifaceName + " already exists."); - return; - } - - final NetworkCapabilities nc = new NetworkCapabilities.Builder(capabilities) - .setNetworkSpecifier(new EthernetNetworkSpecifier(ifaceName)) - .build(); - - if (DBG) { - Log.d(TAG, "addInterface, iface: " + ifaceName + ", capabilities: " + nc); - } - - final NetworkInterfaceState iface = new NetworkInterfaceState( - ifaceName, hwAddress, mHandler, mContext, ipConfig, nc, this, mDeps); - mTrackingInterfaces.put(ifaceName, iface); - updateCapabilityFilter(); - } - - @VisibleForTesting - protected int getInterfaceState(@NonNull String iface) { - final NetworkInterfaceState interfaceState = mTrackingInterfaces.get(iface); - if (interfaceState == null) { - return EthernetManager.STATE_ABSENT; - } else if (!interfaceState.mLinkUp) { - return EthernetManager.STATE_LINK_DOWN; - } else { - return EthernetManager.STATE_LINK_UP; - } - } - - /** - * Update a network's configuration and restart it if necessary. - * - * @param ifaceName the interface name of the network to be updated. - * @param ipConfig the desired {@link IpConfiguration} for the given network or null. If - * {@code null} is passed, the existing IpConfiguration is not updated. - * @param capabilities the desired {@link NetworkCapabilities} for the given network. If - * {@code null} is passed, then the network's current - * {@link NetworkCapabilities} will be used in support of existing APIs as - * the public API does not allow this. - * @param listener an optional {@link INetworkInterfaceOutcomeReceiver} to notify callers of - * completion. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected void updateInterface(@NonNull final String ifaceName, - @Nullable final IpConfiguration ipConfig, - @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (!hasInterface(ifaceName)) { - maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); - return; - } - - final NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName); - iface.updateInterface(ipConfig, capabilities, listener); - mTrackingInterfaces.put(ifaceName, iface); - updateCapabilityFilter(); - } - - private static NetworkCapabilities mixInCapabilities(NetworkCapabilities nc, - NetworkCapabilities addedNc) { - final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(nc); - for (int transport : addedNc.getTransportTypes()) builder.addTransportType(transport); - for (int capability : addedNc.getCapabilities()) builder.addCapability(capability); - return builder.build(); - } - - private void updateCapabilityFilter() { - NetworkCapabilities capabilitiesFilter = createDefaultNetworkCapabilities(); - for (NetworkInterfaceState iface: mTrackingInterfaces.values()) { - capabilitiesFilter = mixInCapabilities(capabilitiesFilter, iface.mCapabilities); - } - - if (DBG) Log.d(TAG, "updateCapabilityFilter: " + capabilitiesFilter); - setCapabilityFilter(capabilitiesFilter); - } - - private static NetworkCapabilities createDefaultNetworkCapabilities() { - return NetworkCapabilities.Builder - .withoutDefaultCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET).build(); - } - - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected void removeInterface(String interfaceName) { - NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName); - if (iface != null) { - iface.maybeSendNetworkManagementCallbackForAbort(); - iface.stop(); - } - - updateCapabilityFilter(); - } - - /** Returns true if state has been modified */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected boolean updateInterfaceLinkState(@NonNull final String ifaceName, final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (!hasInterface(ifaceName)) { - maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); - return false; - } - - if (DBG) { - Log.d(TAG, "updateInterfaceLinkState, iface: " + ifaceName + ", up: " + up); - } - - NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName); - return iface.updateLinkState(up, listener); - } - - private void maybeSendNetworkManagementCallbackForUntracked( - String ifaceName, INetworkInterfaceOutcomeReceiver listener) { - maybeSendNetworkManagementCallback(listener, null, - new EthernetNetworkManagementException( - ifaceName + " can't be updated as it is not available.")); - } - - @VisibleForTesting - protected boolean hasInterface(String ifaceName) { - return mTrackingInterfaces.containsKey(ifaceName); - } - - private NetworkInterfaceState networkForRequest(NetworkRequest request) { - String requestedIface = null; - - NetworkSpecifier specifier = request.getNetworkSpecifier(); - if (specifier instanceof EthernetNetworkSpecifier) { - requestedIface = ((EthernetNetworkSpecifier) specifier) - .getInterfaceName(); - } - - NetworkInterfaceState network = null; - if (!TextUtils.isEmpty(requestedIface)) { - NetworkInterfaceState n = mTrackingInterfaces.get(requestedIface); - if (n != null && request.canBeSatisfiedBy(n.mCapabilities)) { - network = n; - } - } else { - for (NetworkInterfaceState n : mTrackingInterfaces.values()) { - if (request.canBeSatisfiedBy(n.mCapabilities) && n.mLinkUp) { - network = n; - break; - } - } - } - - if (DBG) { - Log.i(TAG, "networkForRequest, request: " + request + ", network: " + network); - } - - return network; - } - - private static void maybeSendNetworkManagementCallback( - @Nullable final INetworkInterfaceOutcomeReceiver listener, - @Nullable final String iface, - @Nullable final EthernetNetworkManagementException e) { - if (null == listener) { - return; - } - - try { - if (iface != null) { - listener.onResult(iface); - } else { - listener.onError(e); - } - } catch (RemoteException re) { - Log.e(TAG, "Can't send onComplete for network management callback", re); - } - } - - @VisibleForTesting - static class NetworkInterfaceState { - final String name; - - private final String mHwAddress; - private final Handler mHandler; - private final Context mContext; - private final NetworkFactory mNetworkFactory; - private final Dependencies mDeps; - - private static String sTcpBufferSizes = null; // Lazy initialized. - - private boolean mLinkUp; - private int mLegacyType; - private LinkProperties mLinkProperties = new LinkProperties(); - - private volatile @Nullable IpClientManager mIpClient; - private @NonNull NetworkCapabilities mCapabilities; - private @Nullable EthernetIpClientCallback mIpClientCallback; - private @Nullable EthernetNetworkAgent mNetworkAgent; - private @Nullable IpConfiguration mIpConfig; - - /** - * A map of TRANSPORT_* types to legacy transport types available for each type an ethernet - * interface could propagate. - * - * There are no legacy type equivalents to LOWPAN or WIFI_AWARE. These types are set to - * TYPE_NONE to match the behavior of their own network factories. - */ - private static final SparseArray sTransports = new SparseArray(); - static { - sTransports.put(NetworkCapabilities.TRANSPORT_ETHERNET, - ConnectivityManager.TYPE_ETHERNET); - sTransports.put(NetworkCapabilities.TRANSPORT_BLUETOOTH, - ConnectivityManager.TYPE_BLUETOOTH); - sTransports.put(NetworkCapabilities.TRANSPORT_WIFI, ConnectivityManager.TYPE_WIFI); - sTransports.put(NetworkCapabilities.TRANSPORT_CELLULAR, - ConnectivityManager.TYPE_MOBILE); - sTransports.put(NetworkCapabilities.TRANSPORT_LOWPAN, ConnectivityManager.TYPE_NONE); - sTransports.put(NetworkCapabilities.TRANSPORT_WIFI_AWARE, - ConnectivityManager.TYPE_NONE); - } - - long refCount = 0; - - private class EthernetIpClientCallback extends IpClientCallbacks { - private final ConditionVariable mIpClientStartCv = new ConditionVariable(false); - private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false); - @Nullable INetworkInterfaceOutcomeReceiver mNetworkManagementListener; - - EthernetIpClientCallback(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - mNetworkManagementListener = listener; - } - - @Override - public void onIpClientCreated(IIpClient ipClient) { - mIpClient = mDeps.makeIpClientManager(ipClient); - mIpClientStartCv.open(); - } - - private void awaitIpClientStart() { - mIpClientStartCv.block(); - } - - private void awaitIpClientShutdown() { - mIpClientShutdownCv.block(); - } - - // At the time IpClient is stopped, an IpClient event may have already been posted on - // the back of the handler and is awaiting execution. Once that event is executed, the - // associated callback object may not be valid anymore - // (NetworkInterfaceState#mIpClientCallback points to a different object / null). - private boolean isCurrentCallback() { - return this == mIpClientCallback; - } - - private void handleIpEvent(final @NonNull Runnable r) { - mHandler.post(() -> { - if (!isCurrentCallback()) { - Log.i(TAG, "Ignoring stale IpClientCallbacks " + this); - return; - } - r.run(); - }); - } - - @Override - public void onProvisioningSuccess(LinkProperties newLp) { - handleIpEvent(() -> onIpLayerStarted(newLp, mNetworkManagementListener)); - } - - @Override - public void onProvisioningFailure(LinkProperties newLp) { - // This cannot happen due to provisioning timeout, because our timeout is 0. It can - // happen due to errors while provisioning or on provisioning loss. - handleIpEvent(() -> onIpLayerStopped(mNetworkManagementListener)); - } - - @Override - public void onLinkPropertiesChange(LinkProperties newLp) { - handleIpEvent(() -> updateLinkProperties(newLp)); - } - - @Override - public void onReachabilityLost(String logMsg) { - handleIpEvent(() -> updateNeighborLostEvent(logMsg)); - } - - @Override - public void onQuit() { - mIpClient = null; - mIpClientShutdownCv.open(); - } - } - - NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context, - @NonNull IpConfiguration ipConfig, @NonNull NetworkCapabilities capabilities, - NetworkFactory networkFactory, Dependencies deps) { - name = ifaceName; - mIpConfig = Objects.requireNonNull(ipConfig); - mCapabilities = Objects.requireNonNull(capabilities); - mLegacyType = getLegacyType(mCapabilities); - mHandler = handler; - mContext = context; - mNetworkFactory = networkFactory; - mDeps = deps; - mHwAddress = hwAddress; - } - - /** - * Determines the legacy transport type from a NetworkCapabilities transport type. Defaults - * to legacy TYPE_NONE if there is no known conversion - */ - private static int getLegacyType(int transport) { - return sTransports.get(transport, ConnectivityManager.TYPE_NONE); - } - - private static int getLegacyType(@NonNull final NetworkCapabilities capabilities) { - final int[] transportTypes = capabilities.getTransportTypes(); - if (transportTypes.length > 0) { - return getLegacyType(transportTypes[0]); - } - - // Should never happen as transport is always one of ETHERNET or a valid override - throw new ConfigurationException("Network Capabilities do not have an associated " - + "transport type."); - } - - private void setCapabilities(@NonNull final NetworkCapabilities capabilities) { - mCapabilities = new NetworkCapabilities(capabilities); - mLegacyType = getLegacyType(mCapabilities); - } - - void updateInterface(@Nullable final IpConfiguration ipConfig, - @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (DBG) { - Log.d(TAG, "updateInterface, iface: " + name - + ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig - + ", capabilities: " + capabilities + ", old capabilities: " + mCapabilities - + ", listener: " + listener - ); - } - - if (null != ipConfig){ - mIpConfig = ipConfig; - } - if (null != capabilities) { - setCapabilities(capabilities); - } - // Send an abort callback if a request is filed before the previous one has completed. - maybeSendNetworkManagementCallbackForAbort(); - // TODO: Update this logic to only do a restart if required. Although a restart may - // be required due to the capabilities or ipConfiguration values, not all - // capabilities changes require a restart. - restart(listener); - } - - boolean isRestricted() { - return !mCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - } - - private void start() { - start(null); - } - - private void start(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (mIpClient != null) { - if (DBG) Log.d(TAG, "IpClient already started"); - return; - } - if (DBG) { - Log.d(TAG, String.format("Starting Ethernet IpClient(%s)", name)); - } - - mIpClientCallback = new EthernetIpClientCallback(listener); - mDeps.makeIpClient(mContext, name, mIpClientCallback); - mIpClientCallback.awaitIpClientStart(); - - if (sTcpBufferSizes == null) { - sTcpBufferSizes = mDeps.getTcpBufferSizesFromResource(mContext); - } - provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes); - } - - void onIpLayerStarted(@NonNull final LinkProperties linkProperties, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (mNetworkAgent != null) { - Log.e(TAG, "Already have a NetworkAgent - aborting new request"); - stop(); - return; - } - mLinkProperties = linkProperties; - - // Create our NetworkAgent. - final NetworkAgentConfig config = new NetworkAgentConfig.Builder() - .setLegacyType(mLegacyType) - .setLegacyTypeName(NETWORK_TYPE) - .setLegacyExtraInfo(mHwAddress) - .build(); - mNetworkAgent = mDeps.makeEthernetNetworkAgent(mContext, mHandler.getLooper(), - mCapabilities, mLinkProperties, config, mNetworkFactory.getProvider(), - new EthernetNetworkAgent.Callbacks() { - @Override - public void onNetworkUnwanted() { - // if mNetworkAgent is null, we have already called stop. - if (mNetworkAgent == null) return; - - if (this == mNetworkAgent.getCallbacks()) { - stop(); - } else { - Log.d(TAG, "Ignoring unwanted as we have a more modern " + - "instance"); - } - } - }); - mNetworkAgent.register(); - mNetworkAgent.markConnected(); - realizeNetworkManagementCallback(name, null); - } - - void onIpLayerStopped(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - // There is no point in continuing if the interface is gone as stop() will be triggered - // by removeInterface() when processed on the handler thread and start() won't - // work for a non-existent interface. - if (null == mDeps.getNetworkInterfaceByName(name)) { - if (DBG) Log.d(TAG, name + " is no longer available."); - // Send a callback in case a provisioning request was in progress. - maybeSendNetworkManagementCallbackForAbort(); - return; - } - restart(listener); - } - - private void maybeSendNetworkManagementCallbackForAbort() { - realizeNetworkManagementCallback(null, - new EthernetNetworkManagementException( - "The IP provisioning request has been aborted.")); - } - - // Must be called on the handler thread - private void realizeNetworkManagementCallback(@Nullable final String iface, - @Nullable final EthernetNetworkManagementException e) { - ensureRunningOnEthernetHandlerThread(); - if (null == mIpClientCallback) { - return; - } - - EthernetNetworkFactory.maybeSendNetworkManagementCallback( - mIpClientCallback.mNetworkManagementListener, iface, e); - // Only send a single callback per listener. - mIpClientCallback.mNetworkManagementListener = null; - } - - private void ensureRunningOnEthernetHandlerThread() { - if (mHandler.getLooper().getThread() != Thread.currentThread()) { - throw new IllegalStateException( - "Not running on the Ethernet thread: " - + Thread.currentThread().getName()); - } - } - - void updateLinkProperties(LinkProperties linkProperties) { - mLinkProperties = linkProperties; - if (mNetworkAgent != null) { - mNetworkAgent.sendLinkPropertiesImpl(linkProperties); - } - } - - void updateNeighborLostEvent(String logMsg) { - Log.i(TAG, "updateNeighborLostEvent " + logMsg); - // Reachability lost will be seen only if the gateway is not reachable. - // Since ethernet FW doesn't have the mechanism to scan for new networks - // like WiFi, simply restart. - // If there is a better network, that will become default and apps - // will be able to use internet. If ethernet gets connected again, - // and has backhaul connectivity, it will become default. - restart(); - } - - /** Returns true if state has been modified */ - boolean updateLinkState(final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (mLinkUp == up) { - EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, null, - new EthernetNetworkManagementException( - "No changes with requested link state " + up + " for " + name)); - return false; - } - mLinkUp = up; - - if (!up) { // was up, goes down - // Send an abort on a provisioning request callback if necessary before stopping. - maybeSendNetworkManagementCallbackForAbort(); - stop(); - // If only setting the interface down, send a callback to signal completion. - EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null); - } else { // was down, goes up - stop(); - start(listener); - } - - return true; - } - - void stop() { - // Invalidate all previous start requests - if (mIpClient != null) { - mIpClient.shutdown(); - mIpClientCallback.awaitIpClientShutdown(); - mIpClient = null; - } - mIpClientCallback = null; - - if (mNetworkAgent != null) { - mNetworkAgent.unregister(); - mNetworkAgent = null; - } - mLinkProperties.clear(); - } - - private static void provisionIpClient(@NonNull final IpClientManager ipClient, - @NonNull final IpConfiguration config, @NonNull final String tcpBufferSizes) { - if (config.getProxySettings() == ProxySettings.STATIC || - config.getProxySettings() == ProxySettings.PAC) { - ipClient.setHttpProxy(config.getHttpProxy()); - } - - if (!TextUtils.isEmpty(tcpBufferSizes)) { - ipClient.setTcpBufferSizes(tcpBufferSizes); - } - - ipClient.startProvisioning(createProvisioningConfiguration(config)); - } - - private static ProvisioningConfiguration createProvisioningConfiguration( - @NonNull final IpConfiguration config) { - if (config.getIpAssignment() == IpAssignment.STATIC) { - return new ProvisioningConfiguration.Builder() - .withStaticConfiguration(config.getStaticIpConfiguration()) - .build(); - } - return new ProvisioningConfiguration.Builder() - .withProvisioningTimeoutMs(0) - .build(); - } - - void restart() { - restart(null); - } - - void restart(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (DBG) Log.d(TAG, "reconnecting Ethernet"); - stop(); - start(listener); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "{ " - + "refCount: " + refCount + ", " - + "iface: " + name + ", " - + "up: " + mLinkUp + ", " - + "hwAddress: " + mHwAddress + ", " - + "networkCapabilities: " + mCapabilities + ", " - + "networkAgent: " + mNetworkAgent + ", " - + "ipClient: " + mIpClient + "," - + "linkProperties: " + mLinkProperties - + "}"; - } - } - - void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { - super.dump(fd, pw, args); - pw.println(getClass().getSimpleName()); - pw.println("Tracking interfaces:"); - pw.increaseIndent(); - for (String iface: mTrackingInterfaces.keySet()) { - NetworkInterfaceState ifaceState = mTrackingInterfaces.get(iface); - pw.println(iface + ":" + ifaceState); - pw.increaseIndent(); - if (null == ifaceState.mIpClient) { - pw.println("IpClient is null"); - } - pw.decreaseIndent(); - } - pw.decreaseIndent(); - } -} diff --git a/java/com/android/server/ethernet/EthernetService.java b/java/com/android/server/ethernet/EthernetService.java deleted file mode 100644 index d405fd5..0000000 --- a/java/com/android/server/ethernet/EthernetService.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2014 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.content.Context; -import android.net.INetd; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; - -import java.util.Objects; - -// TODO: consider renaming EthernetServiceImpl to EthernetService and deleting this file. -public final class EthernetService { - private static final String TAG = "EthernetService"; - private static final String THREAD_NAME = "EthernetServiceThread"; - - private static INetd getNetd(Context context) { - final INetd netd = - INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)); - Objects.requireNonNull(netd, "could not get netd instance"); - return netd; - } - - public static EthernetServiceImpl create(Context context) { - final HandlerThread handlerThread = new HandlerThread(THREAD_NAME); - handlerThread.start(); - final Handler handler = new Handler(handlerThread.getLooper()); - final EthernetNetworkFactory factory = new EthernetNetworkFactory(handler, context); - return new EthernetServiceImpl(context, handler, - new EthernetTracker(context, handler, factory, getNetd(context))); - } -} diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java deleted file mode 100644 index 5e830ad..0000000 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (C) 2014 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.NetworkCapabilities.TRANSPORT_TEST; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.pm.PackageManager; -import android.net.IEthernetManager; -import android.net.IEthernetServiceListener; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.ITetheredInterfaceCallback; -import android.net.EthernetNetworkUpdateRequest; -import android.net.IpConfiguration; -import android.net.NetworkCapabilities; -import android.os.Binder; -import android.os.Handler; -import android.os.RemoteException; -import android.util.Log; -import android.util.PrintWriterPrinter; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.net.module.util.PermissionUtils; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * EthernetServiceImpl handles remote Ethernet operation requests by implementing - * the IEthernetManager interface. - */ -public class EthernetServiceImpl extends IEthernetManager.Stub { - private static final String TAG = "EthernetServiceImpl"; - - @VisibleForTesting - final AtomicBoolean mStarted = new AtomicBoolean(false); - private final Context mContext; - private final Handler mHandler; - private final EthernetTracker mTracker; - - EthernetServiceImpl(@NonNull final Context context, @NonNull final Handler handler, - @NonNull final EthernetTracker tracker) { - mContext = context; - mHandler = handler; - mTracker = tracker; - } - - private void enforceAutomotiveDevice(final @NonNull String methodName) { - PermissionUtils.enforceSystemFeature(mContext, PackageManager.FEATURE_AUTOMOTIVE, - methodName + " is only available on automotive devices."); - } - - private boolean checkUseRestrictedNetworksPermission() { - return PermissionUtils.checkAnyPermissionOf(mContext, - android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS); - } - - public void start() { - Log.i(TAG, "Starting Ethernet service"); - mTracker.start(); - mStarted.set(true); - } - - private void throwIfEthernetNotStarted() { - if (!mStarted.get()) { - throw new IllegalStateException("System isn't ready to change ethernet configurations"); - } - } - - @Override - public String[] getAvailableInterfaces() throws RemoteException { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - return mTracker.getInterfaces(checkUseRestrictedNetworksPermission()); - } - - /** - * Get Ethernet configuration - * @return the Ethernet Configuration, contained in {@link IpConfiguration}. - */ - @Override - public IpConfiguration getConfiguration(String iface) { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - if (mTracker.isRestrictedInterface(iface)) { - PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG); - } - - return new IpConfiguration(mTracker.getIpConfiguration(iface)); - } - - /** - * Set Ethernet configuration - */ - @Override - public void setConfiguration(String iface, IpConfiguration config) { - throwIfEthernetNotStarted(); - - PermissionUtils.enforceNetworkStackPermission(mContext); - if (mTracker.isRestrictedInterface(iface)) { - PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG); - } - - // TODO: this does not check proxy settings, gateways, etc. - // Fix this by making IpConfiguration a complete representation of static configuration. - mTracker.updateIpConfiguration(iface, new IpConfiguration(config)); - } - - /** - * Indicates whether given interface is available. - */ - @Override - public boolean isAvailable(String iface) { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - if (mTracker.isRestrictedInterface(iface)) { - PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG); - } - - return mTracker.isTrackingInterface(iface); - } - - /** - * Adds a listener. - * @param listener A {@link IEthernetServiceListener} to add. - */ - public void addListener(IEthernetServiceListener listener) throws RemoteException { - Objects.requireNonNull(listener, "listener must not be null"); - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - mTracker.addListener(listener, checkUseRestrictedNetworksPermission()); - } - - /** - * Removes a listener. - * @param listener A {@link IEthernetServiceListener} to remove. - */ - public void removeListener(IEthernetServiceListener listener) { - if (listener == null) { - throw new IllegalArgumentException("listener must not be null"); - } - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - mTracker.removeListener(listener); - } - - @Override - public void setIncludeTestInterfaces(boolean include) { - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - mTracker.setIncludeTestInterfaces(include); - } - - @Override - public void requestTetheredInterface(ITetheredInterfaceCallback callback) { - Objects.requireNonNull(callback, "callback must not be null"); - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - mTracker.requestTetheredInterface(callback); - } - - @Override - public void releaseTetheredInterface(ITetheredInterfaceCallback callback) { - Objects.requireNonNull(callback, "callback must not be null"); - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - mTracker.releaseTetheredInterface(callback); - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump EthernetService from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; - } - - pw.println("Current Ethernet state: "); - pw.increaseIndent(); - mTracker.dump(fd, pw, args); - pw.decreaseIndent(); - - pw.println("Handler:"); - pw.increaseIndent(); - mHandler.dump(new PrintWriterPrinter(pw), "EthernetServiceImpl"); - pw.decreaseIndent(); - } - - private void enforceNetworkManagementPermission() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_ETHERNET_NETWORKS, - "EthernetServiceImpl"); - } - - private void enforceManageTestNetworksPermission() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_TEST_NETWORKS, - "EthernetServiceImpl"); - } - - private void maybeValidateTestCapabilities(final String iface, - @Nullable final NetworkCapabilities nc) { - if (!mTracker.isValidTestInterface(iface)) { - return; - } - // For test interfaces, only null or capabilities that include TRANSPORT_TEST are - // allowed. - if (nc != null && !nc.hasTransport(TRANSPORT_TEST)) { - throw new IllegalArgumentException( - "Updates to test interfaces must have NetworkCapabilities.TRANSPORT_TEST."); - } - } - - private void enforceAdminPermission(final String iface, boolean enforceAutomotive, - final String logMessage) { - if (mTracker.isValidTestInterface(iface)) { - enforceManageTestNetworksPermission(); - } else { - enforceNetworkManagementPermission(); - if (enforceAutomotive) { - enforceAutomotiveDevice(logMessage); - } - } - } - - @Override - public void updateConfiguration(@NonNull final String iface, - @NonNull final EthernetNetworkUpdateRequest request, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - Objects.requireNonNull(iface); - Objects.requireNonNull(request); - throwIfEthernetNotStarted(); - - // TODO: validate that iface is listed in overlay config_ethernet_interfaces - // only automotive devices are allowed to set the NetworkCapabilities using this API - enforceAdminPermission(iface, request.getNetworkCapabilities() != null, - "updateConfiguration() with non-null capabilities"); - maybeValidateTestCapabilities(iface, request.getNetworkCapabilities()); - - mTracker.updateConfiguration( - iface, request.getIpConfiguration(), request.getNetworkCapabilities(), listener); - } - - @Override - public void connectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - Log.i(TAG, "connectNetwork called with: iface=" + iface + ", listener=" + listener); - Objects.requireNonNull(iface); - throwIfEthernetNotStarted(); - - enforceAdminPermission(iface, true, "connectNetwork()"); - - mTracker.connectNetwork(iface, listener); - } - - @Override - public void disconnectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - Log.i(TAG, "disconnectNetwork called with: iface=" + iface + ", listener=" + listener); - Objects.requireNonNull(iface); - throwIfEthernetNotStarted(); - - enforceAdminPermission(iface, true, "connectNetwork()"); - - mTracker.disconnectNetwork(iface, listener); - } - - @Override - public void setEthernetEnabled(boolean enabled) { - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - - mTracker.setEthernetEnabled(enabled); - } - - @Override - public List getInterfaceList() { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - return mTracker.getInterfaceList(); - } -} diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java deleted file mode 100644 index abb1635..0000000 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ /dev/null @@ -1,943 +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. - * - *

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. - * - *

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 mNetworkCapabilities = - new ConcurrentHashMap<>(); - private final ConcurrentHashMap 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 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 { - @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 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 getInterfaceList() { - final List interfaceList = new ArrayList(); - 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 - * ;[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= gateway= dns= - * 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 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; - } - } -} diff --git a/tests/Android.bp b/tests/Android.bp deleted file mode 100644 index 6cfebdc..0000000 --- a/tests/Android.bp +++ /dev/null @@ -1,53 +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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -// TODO: merge the tests into service-connectivity tests after -// ethernet service migration completes. So far just import the -// ethernet service source to fix the dependencies. -android_test { - name: "EthernetServiceTests", - - srcs: [ - ":ethernet-service-updatable-sources", - ":services.connectivity-ethernet-sources", - "java/**/*.java", - ], - - certificate: "platform", - platform_apis: true, - - libs: [ - "android.test.runner", - "android.test.base", - "android.test.mock", - "framework-connectivity.impl", - "framework-connectivity-t.impl", - "ServiceConnectivityResources", - ], - - static_libs: [ - "androidx.test.rules", - "frameworks-base-testutils", - "mockito-target-minus-junit4", - "net-tests-utils", - "services.core", - "services.net", - ], - test_suites: ["general-tests"], -} diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml deleted file mode 100644 index cd875b0..0000000 --- a/tests/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java deleted file mode 100644 index 4d3e4d3..0000000 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ /dev/null @@ -1,783 +0,0 @@ -/* - * Copyright (C) 2020 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 com.android.testutils.DevSdkIgnoreRuleKt.SC_V2; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.app.test.MockAnswerUtil.AnswerWithArguments; -import android.content.Context; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.EthernetNetworkSpecifier; -import android.net.EthernetNetworkManagementException; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.IpConfiguration; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkAgentConfig; -import android.net.NetworkCapabilities; -import android.net.NetworkProvider; -import android.net.NetworkRequest; -import android.net.StaticIpConfiguration; -import android.net.ip.IpClientCallbacks; -import android.net.ip.IpClientManager; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.test.TestLooper; -import android.util.Pair; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.connectivity.resources.R; -import com.android.net.module.util.InterfaceParams; - -import com.android.testutils.DevSdkIgnoreRule; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class EthernetNetworkFactoryTest { - private static final int TIMEOUT_MS = 2_000; - private static final String TEST_IFACE = "test123"; - private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; - private static final String IP_ADDR = "192.0.2.2/25"; - private static final LinkAddress LINK_ADDR = new LinkAddress(IP_ADDR); - private static final String HW_ADDR = "01:02:03:04:05:06"; - private TestLooper mLooper; - private Handler mHandler; - private EthernetNetworkFactory mNetFactory = null; - private IpClientCallbacks mIpClientCallbacks; - @Mock private Context mContext; - @Mock private Resources mResources; - @Mock private EthernetNetworkFactory.Dependencies mDeps; - @Mock private IpClientManager mIpClient; - @Mock private EthernetNetworkAgent mNetworkAgent; - @Mock private InterfaceParams mInterfaceParams; - @Mock private Network mMockNetwork; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - setupNetworkAgentMock(); - setupIpClientMock(); - setupContext(); - } - - //TODO: Move away from usage of TestLooper in order to move this logic back into @Before. - private void initEthernetNetworkFactory() { - mLooper = new TestLooper(); - mHandler = new Handler(mLooper.getLooper()); - mNetFactory = new EthernetNetworkFactory(mHandler, mContext, mDeps); - } - - private void setupNetworkAgentMock() { - when(mDeps.makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any())) - .thenAnswer(new AnswerWithArguments() { - public EthernetNetworkAgent answer( - Context context, - Looper looper, - NetworkCapabilities nc, - LinkProperties lp, - NetworkAgentConfig config, - NetworkProvider provider, - EthernetNetworkAgent.Callbacks cb) { - when(mNetworkAgent.getCallbacks()).thenReturn(cb); - when(mNetworkAgent.getNetwork()) - .thenReturn(mMockNetwork); - return mNetworkAgent; - } - } - ); - } - - private void setupIpClientMock() throws Exception { - doAnswer(inv -> { - // these tests only support one concurrent IpClient, so make sure we do not accidentally - // create a mess. - assertNull("An IpClient has already been created.", mIpClientCallbacks); - - mIpClientCallbacks = inv.getArgument(2); - mIpClientCallbacks.onIpClientCreated(null); - mLooper.dispatchAll(); - return null; - }).when(mDeps).makeIpClient(any(Context.class), anyString(), any()); - - doAnswer(inv -> { - mIpClientCallbacks.onQuit(); - mLooper.dispatchAll(); - mIpClientCallbacks = null; - return null; - }).when(mIpClient).shutdown(); - - when(mDeps.makeIpClientManager(any())).thenReturn(mIpClient); - } - - private void triggerOnProvisioningSuccess() { - mIpClientCallbacks.onProvisioningSuccess(new LinkProperties()); - mLooper.dispatchAll(); - } - - private void triggerOnProvisioningFailure() { - mIpClientCallbacks.onProvisioningFailure(new LinkProperties()); - mLooper.dispatchAll(); - } - - private void triggerOnReachabilityLost() { - mIpClientCallbacks.onReachabilityLost("ReachabilityLost"); - mLooper.dispatchAll(); - } - - private void setupContext() { - when(mDeps.getTcpBufferSizesFromResource(eq(mContext))).thenReturn(""); - } - - @After - public void tearDown() { - // looper is shared with the network agents, so there may still be messages to dispatch on - // tear down. - mLooper.dispatchAll(); - } - - private NetworkCapabilities createDefaultFilterCaps() { - return NetworkCapabilities.Builder.withoutDefaultCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .build(); - } - - private NetworkCapabilities.Builder createInterfaceCapsBuilder(final int transportType) { - return new NetworkCapabilities.Builder() - .addTransportType(transportType) - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); - } - - private NetworkRequest.Builder createDefaultRequestBuilder() { - return new NetworkRequest.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - } - - private NetworkRequest createDefaultRequest() { - return createDefaultRequestBuilder().build(); - } - - private IpConfiguration createDefaultIpConfig() { - IpConfiguration ipConfig = new IpConfiguration(); - ipConfig.setIpAssignment(IpConfiguration.IpAssignment.DHCP); - ipConfig.setProxySettings(IpConfiguration.ProxySettings.NONE); - return ipConfig; - } - - /** - * Create an {@link IpConfiguration} with an associated {@link StaticIpConfiguration}. - * - * @return {@link IpConfiguration} with its {@link StaticIpConfiguration} set. - */ - private IpConfiguration createStaticIpConfig() { - final IpConfiguration ipConfig = new IpConfiguration(); - ipConfig.setIpAssignment(IpConfiguration.IpAssignment.STATIC); - ipConfig.setStaticIpConfiguration( - new StaticIpConfiguration.Builder().setIpAddress(LINK_ADDR).build()); - return ipConfig; - } - - // creates an interface with provisioning in progress (since updating the interface link state - // automatically starts the provisioning process) - private void createInterfaceUndergoingProvisioning(String iface) { - // Default to the ethernet transport type. - createInterfaceUndergoingProvisioning(iface, NetworkCapabilities.TRANSPORT_ETHERNET); - } - - private void createInterfaceUndergoingProvisioning( - @NonNull final String iface, final int transportType) { - final IpConfiguration ipConfig = createDefaultIpConfig(); - mNetFactory.addInterface(iface, HW_ADDR, ipConfig, - createInterfaceCapsBuilder(transportType).build()); - assertTrue(mNetFactory.updateInterfaceLinkState(iface, true, NULL_LISTENER)); - verifyStart(ipConfig); - clearInvocations(mDeps); - clearInvocations(mIpClient); - } - - // creates a provisioned interface - private void createAndVerifyProvisionedInterface(String iface) throws Exception { - // Default to the ethernet transport type. - createAndVerifyProvisionedInterface(iface, NetworkCapabilities.TRANSPORT_ETHERNET, - ConnectivityManager.TYPE_ETHERNET); - } - - private void createVerifyAndRemoveProvisionedInterface(final int transportType, - final int expectedLegacyType) throws Exception { - createAndVerifyProvisionedInterface(TEST_IFACE, transportType, - expectedLegacyType); - mNetFactory.removeInterface(TEST_IFACE); - } - - private void createAndVerifyProvisionedInterface( - @NonNull final String iface, final int transportType, final int expectedLegacyType) - throws Exception { - createInterfaceUndergoingProvisioning(iface, transportType); - triggerOnProvisioningSuccess(); - // provisioning succeeded, verify that the network agent is created, registered, marked - // as connected and legacy type are correctly set. - final ArgumentCaptor ncCaptor = ArgumentCaptor.forClass( - NetworkCapabilities.class); - verify(mDeps).makeEthernetNetworkAgent(any(), any(), ncCaptor.capture(), any(), - argThat(x -> x.getLegacyType() == expectedLegacyType), any(), any()); - assertEquals( - new EthernetNetworkSpecifier(iface), ncCaptor.getValue().getNetworkSpecifier()); - verifyNetworkAgentRegistersAndConnects(); - clearInvocations(mDeps); - clearInvocations(mNetworkAgent); - } - - // creates an unprovisioned interface - private void createUnprovisionedInterface(String iface) throws Exception { - // To create an unprovisioned interface, provision and then "stop" it, i.e. stop its - // NetworkAgent and IpClient. One way this can be done is by provisioning an interface and - // then calling onNetworkUnwanted. - createAndVerifyProvisionedInterface(iface); - - mNetworkAgent.getCallbacks().onNetworkUnwanted(); - mLooper.dispatchAll(); - verifyStop(); - - clearInvocations(mIpClient); - clearInvocations(mNetworkAgent); - } - - @Test - public void testAcceptRequest() throws Exception { - initEthernetNetworkFactory(); - createInterfaceUndergoingProvisioning(TEST_IFACE); - assertTrue(mNetFactory.acceptRequest(createDefaultRequest())); - - NetworkRequest wifiRequest = createDefaultRequestBuilder() - .removeTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build(); - assertFalse(mNetFactory.acceptRequest(wifiRequest)); - } - - @Test - public void testUpdateInterfaceLinkStateForActiveProvisioningInterface() throws Exception { - initEthernetNetworkFactory(); - createInterfaceUndergoingProvisioning(TEST_IFACE); - final TestNetworkManagementListener listener = new TestNetworkManagementListener(); - - // verify that the IpClient gets shut down when interface state changes to down. - final boolean ret = - mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */, listener); - - assertTrue(ret); - verify(mIpClient).shutdown(); - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @Test - public void testUpdateInterfaceLinkStateForProvisionedInterface() throws Exception { - initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - final TestNetworkManagementListener listener = new TestNetworkManagementListener(); - - final boolean ret = - mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */, listener); - - assertTrue(ret); - verifyStop(); - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @Test - public void testUpdateInterfaceLinkStateForUnprovisionedInterface() throws Exception { - initEthernetNetworkFactory(); - createUnprovisionedInterface(TEST_IFACE); - final TestNetworkManagementListener listener = new TestNetworkManagementListener(); - - final boolean ret = - mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */, listener); - - assertTrue(ret); - // There should not be an active IPClient or NetworkAgent. - verify(mDeps, never()).makeIpClient(any(), any(), any()); - verify(mDeps, never()) - .makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any()); - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @Test - public void testUpdateInterfaceLinkStateForNonExistingInterface() throws Exception { - initEthernetNetworkFactory(); - final TestNetworkManagementListener listener = new TestNetworkManagementListener(); - - // if interface was never added, link state cannot be updated. - final boolean ret = - mNetFactory.updateInterfaceLinkState(TEST_IFACE, true /* up */, listener); - - assertFalse(ret); - verifyNoStopOrStart(); - listener.expectOnErrorWithMessage("can't be updated as it is not available"); - } - - @Test - public void testUpdateInterfaceLinkStateWithNoChanges() throws Exception { - initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - final TestNetworkManagementListener listener = new TestNetworkManagementListener(); - - final boolean ret = - mNetFactory.updateInterfaceLinkState(TEST_IFACE, true /* up */, listener); - - assertFalse(ret); - verifyNoStopOrStart(); - listener.expectOnErrorWithMessage("No changes"); - } - - @Test - public void testNeedNetworkForOnProvisionedInterface() throws Exception { - initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - mNetFactory.needNetworkFor(createDefaultRequest()); - verify(mIpClient, never()).startProvisioning(any()); - } - - @Test - public void testNeedNetworkForOnUnprovisionedInterface() throws Exception { - initEthernetNetworkFactory(); - createUnprovisionedInterface(TEST_IFACE); - mNetFactory.needNetworkFor(createDefaultRequest()); - verify(mIpClient).startProvisioning(any()); - - triggerOnProvisioningSuccess(); - verifyNetworkAgentRegistersAndConnects(); - } - - @Test - public void testNeedNetworkForOnInterfaceUndergoingProvisioning() throws Exception { - initEthernetNetworkFactory(); - createInterfaceUndergoingProvisioning(TEST_IFACE); - mNetFactory.needNetworkFor(createDefaultRequest()); - verify(mIpClient, never()).startProvisioning(any()); - - triggerOnProvisioningSuccess(); - verifyNetworkAgentRegistersAndConnects(); - } - - @Test - public void testProvisioningLoss() throws Exception { - initEthernetNetworkFactory(); - when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams); - createAndVerifyProvisionedInterface(TEST_IFACE); - - triggerOnProvisioningFailure(); - verifyStop(); - // provisioning loss should trigger a retry, since the interface is still there - verify(mIpClient).startProvisioning(any()); - } - - @Test - public void testProvisioningLossForDisappearedInterface() throws Exception { - initEthernetNetworkFactory(); - // mocked method returns null by default, but just to be explicit in the test: - when(mDeps.getNetworkInterfaceByName(eq(TEST_IFACE))).thenReturn(null); - - createAndVerifyProvisionedInterface(TEST_IFACE); - triggerOnProvisioningFailure(); - - // the interface disappeared and getNetworkInterfaceByName returns null, we should not retry - verify(mIpClient, never()).startProvisioning(any()); - verifyNoStopOrStart(); - } - - private void verifyNoStopOrStart() { - verify(mNetworkAgent, never()).register(); - verify(mIpClient, never()).shutdown(); - verify(mNetworkAgent, never()).unregister(); - verify(mIpClient, never()).startProvisioning(any()); - } - - @Test - public void testIpClientIsNotStartedWhenLinkIsDown() throws Exception { - initEthernetNetworkFactory(); - createUnprovisionedInterface(TEST_IFACE); - mNetFactory.updateInterfaceLinkState(TEST_IFACE, false, NULL_LISTENER); - - mNetFactory.needNetworkFor(createDefaultRequest()); - - verify(mDeps, never()).makeIpClient(any(), any(), any()); - - // BUG(b/191854824): requesting a network with a specifier (Android Auto use case) should - // not start an IpClient when the link is down, but fixing this may make matters worse by - // tiggering b/197548738. - NetworkRequest specificNetRequest = new NetworkRequest.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .setNetworkSpecifier(new EthernetNetworkSpecifier(TEST_IFACE)) - .build(); - mNetFactory.needNetworkFor(specificNetRequest); - mNetFactory.releaseNetworkFor(specificNetRequest); - - mNetFactory.updateInterfaceLinkState(TEST_IFACE, true, NULL_LISTENER); - // TODO: change to once when b/191854824 is fixed. - verify(mDeps, times(2)).makeIpClient(any(), eq(TEST_IFACE), any()); - } - - @Test - public void testLinkPropertiesChanged() throws Exception { - initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - - LinkProperties lp = new LinkProperties(); - mIpClientCallbacks.onLinkPropertiesChange(lp); - mLooper.dispatchAll(); - verify(mNetworkAgent).sendLinkPropertiesImpl(same(lp)); - } - - @Test - public void testNetworkUnwanted() throws Exception { - initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - - mNetworkAgent.getCallbacks().onNetworkUnwanted(); - mLooper.dispatchAll(); - verifyStop(); - } - - @Test - public void testNetworkUnwantedWithStaleNetworkAgent() throws Exception { - initEthernetNetworkFactory(); - // ensures provisioning is restarted after provisioning loss - when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams); - createAndVerifyProvisionedInterface(TEST_IFACE); - - EthernetNetworkAgent.Callbacks oldCbs = mNetworkAgent.getCallbacks(); - // replace network agent in EthernetNetworkFactory - // Loss of provisioning will restart the ip client and network agent. - triggerOnProvisioningFailure(); - verify(mDeps).makeIpClient(any(), any(), any()); - - triggerOnProvisioningSuccess(); - verify(mDeps).makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any()); - - // verify that unwanted is ignored - clearInvocations(mIpClient); - clearInvocations(mNetworkAgent); - oldCbs.onNetworkUnwanted(); - verify(mIpClient, never()).shutdown(); - verify(mNetworkAgent, never()).unregister(); - } - - @Test - public void testTransportOverrideIsCorrectlySet() throws Exception { - initEthernetNetworkFactory(); - // createProvisionedInterface() has verifications in place for transport override - // functionality which for EthernetNetworkFactory is network score and legacy type mappings. - createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_ETHERNET, - ConnectivityManager.TYPE_ETHERNET); - createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_BLUETOOTH, - ConnectivityManager.TYPE_BLUETOOTH); - createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_WIFI, - ConnectivityManager.TYPE_WIFI); - createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_CELLULAR, - ConnectivityManager.TYPE_MOBILE); - createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_LOWPAN, - ConnectivityManager.TYPE_NONE); - createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_WIFI_AWARE, - ConnectivityManager.TYPE_NONE); - createVerifyAndRemoveProvisionedInterface(NetworkCapabilities.TRANSPORT_TEST, - ConnectivityManager.TYPE_NONE); - } - - @Test - public void testReachabilityLoss() throws Exception { - initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - - triggerOnReachabilityLost(); - - // Reachability loss should trigger a stop and start, since the interface is still there - verifyRestart(createDefaultIpConfig()); - } - - private IpClientCallbacks getStaleIpClientCallbacks() throws Exception { - createAndVerifyProvisionedInterface(TEST_IFACE); - final IpClientCallbacks staleIpClientCallbacks = mIpClientCallbacks; - mNetFactory.removeInterface(TEST_IFACE); - verifyStop(); - assertNotSame(mIpClientCallbacks, staleIpClientCallbacks); - return staleIpClientCallbacks; - } - - @Test - public void testIgnoreOnIpLayerStartedCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - - staleIpClientCallbacks.onProvisioningSuccess(new LinkProperties()); - mLooper.dispatchAll(); - - verify(mIpClient, never()).startProvisioning(any()); - verify(mNetworkAgent, never()).register(); - } - - @Test - public void testIgnoreOnIpLayerStoppedCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - - staleIpClientCallbacks.onProvisioningFailure(new LinkProperties()); - mLooper.dispatchAll(); - - verify(mIpClient, never()).startProvisioning(any()); - } - - @Test - public void testIgnoreLinkPropertiesCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - final LinkProperties lp = new LinkProperties(); - - staleIpClientCallbacks.onLinkPropertiesChange(lp); - mLooper.dispatchAll(); - - verify(mNetworkAgent, never()).sendLinkPropertiesImpl(eq(lp)); - } - - @Test - public void testIgnoreNeighborLossCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - - staleIpClientCallbacks.onReachabilityLost("Neighbor Lost"); - mLooper.dispatchAll(); - - verify(mIpClient, never()).startProvisioning(any()); - verify(mNetworkAgent, never()).register(); - } - - private void verifyRestart(@NonNull final IpConfiguration ipConfig) { - verifyStop(); - verifyStart(ipConfig); - } - - private void verifyStart(@NonNull final IpConfiguration ipConfig) { - verify(mDeps).makeIpClient(any(Context.class), anyString(), any()); - verify(mIpClient).startProvisioning( - argThat(x -> Objects.equals(x.mStaticIpConfig, ipConfig.getStaticIpConfiguration())) - ); - } - - private void verifyStop() { - verify(mIpClient).shutdown(); - verify(mNetworkAgent).unregister(); - } - - private void verifyNetworkAgentRegistersAndConnects() { - verify(mNetworkAgent).register(); - verify(mNetworkAgent).markConnected(); - } - - private static final class TestNetworkManagementListener - implements INetworkInterfaceOutcomeReceiver { - private final CompletableFuture mResult = new CompletableFuture<>(); - private final CompletableFuture mError = - new CompletableFuture<>(); - - @Override - public void onResult(@NonNull String iface) { - mResult.complete(iface); - } - - @Override - public void onError(@NonNull EthernetNetworkManagementException exception) { - mError.complete(exception); - } - - String expectOnResult() throws Exception { - return mResult.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } - - EthernetNetworkManagementException expectOnError() throws Exception { - return mError.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } - - void expectOnErrorWithMessage(String msg) throws Exception { - assertTrue(expectOnError().getMessage().contains(msg)); - } - - @Override - public IBinder asBinder() { - return null; - } - } - - @Test - public void testUpdateInterfaceCallsListenerCorrectlyOnSuccess() throws Exception { - initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - final NetworkCapabilities capabilities = createDefaultFilterCaps(); - final IpConfiguration ipConfiguration = createStaticIpConfig(); - final TestNetworkManagementListener listener = new TestNetworkManagementListener(); - - mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); - triggerOnProvisioningSuccess(); - - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available - @Test - public void testUpdateInterfaceAbortsOnConcurrentRemoveInterface() throws Exception { - initEthernetNetworkFactory(); - verifyNetworkManagementCallIsAbortedWhenInterrupted( - TEST_IFACE, - () -> mNetFactory.removeInterface(TEST_IFACE)); - } - - @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available - @Test - public void testUpdateInterfaceAbortsOnConcurrentUpdateInterfaceLinkState() throws Exception { - initEthernetNetworkFactory(); - verifyNetworkManagementCallIsAbortedWhenInterrupted( - TEST_IFACE, - () -> mNetFactory.updateInterfaceLinkState(TEST_IFACE, false, NULL_LISTENER)); - } - - @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available - @Test - public void testUpdateInterfaceCallsListenerCorrectlyOnConcurrentRequests() throws Exception { - initEthernetNetworkFactory(); - final NetworkCapabilities capabilities = createDefaultFilterCaps(); - final IpConfiguration ipConfiguration = createStaticIpConfig(); - final TestNetworkManagementListener successfulListener = - new TestNetworkManagementListener(); - - // If two calls come in before the first one completes, the first listener will be aborted - // and the second one will be successful. - verifyNetworkManagementCallIsAbortedWhenInterrupted( - TEST_IFACE, - () -> { - mNetFactory.updateInterface( - TEST_IFACE, ipConfiguration, capabilities, successfulListener); - triggerOnProvisioningSuccess(); - }); - - assertEquals(successfulListener.expectOnResult(), TEST_IFACE); - } - - private void verifyNetworkManagementCallIsAbortedWhenInterrupted( - @NonNull final String iface, - @NonNull final Runnable interruptingRunnable) throws Exception { - createAndVerifyProvisionedInterface(iface); - final NetworkCapabilities capabilities = createDefaultFilterCaps(); - final IpConfiguration ipConfiguration = createStaticIpConfig(); - final TestNetworkManagementListener failedListener = new TestNetworkManagementListener(); - - // An active update request will be aborted on interrupt prior to provisioning completion. - mNetFactory.updateInterface(iface, ipConfiguration, capabilities, failedListener); - interruptingRunnable.run(); - - failedListener.expectOnErrorWithMessage("aborted"); - } - - @Test - public void testUpdateInterfaceRestartsAgentCorrectly() throws Exception { - initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - final NetworkCapabilities capabilities = createDefaultFilterCaps(); - final IpConfiguration ipConfiguration = createStaticIpConfig(); - final TestNetworkManagementListener listener = new TestNetworkManagementListener(); - - mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); - triggerOnProvisioningSuccess(); - - assertEquals(listener.expectOnResult(), TEST_IFACE); - verify(mDeps).makeEthernetNetworkAgent(any(), any(), - eq(capabilities), any(), any(), any(), any()); - verifyRestart(ipConfiguration); - } - - @Test - public void testUpdateInterfaceForNonExistingInterface() throws Exception { - initEthernetNetworkFactory(); - // No interface exists due to not calling createAndVerifyProvisionedInterface(...). - final NetworkCapabilities capabilities = createDefaultFilterCaps(); - final IpConfiguration ipConfiguration = createStaticIpConfig(); - final TestNetworkManagementListener listener = new TestNetworkManagementListener(); - - mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); - - verifyNoStopOrStart(); - listener.expectOnErrorWithMessage("can't be updated as it is not available"); - } - - @Test - public void testUpdateInterfaceWithNullIpConfiguration() throws Exception { - initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - - final IpConfiguration initialIpConfig = createStaticIpConfig(); - mNetFactory.updateInterface(TEST_IFACE, initialIpConfig, null /*capabilities*/, - null /*listener*/); - triggerOnProvisioningSuccess(); - verifyRestart(initialIpConfig); - - // TODO: have verifyXyz functions clear invocations. - clearInvocations(mDeps); - clearInvocations(mIpClient); - clearInvocations(mNetworkAgent); - - - // verify that sending a null ipConfig does not update the current ipConfig. - mNetFactory.updateInterface(TEST_IFACE, null /*ipConfig*/, null /*capabilities*/, - null /*listener*/); - triggerOnProvisioningSuccess(); - verifyRestart(initialIpConfig); - } -} diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java deleted file mode 100644 index dd1f1ed..0000000 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright (C) 2021 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.NetworkCapabilities.TRANSPORT_TEST; - -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.fail; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.Manifest; -import android.annotation.NonNull; -import android.content.Context; -import android.content.pm.PackageManager; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.EthernetNetworkUpdateRequest; -import android.net.IpConfiguration; -import android.net.NetworkCapabilities; -import android.os.Handler; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class EthernetServiceImplTest { - private static final String TEST_IFACE = "test123"; - private static final EthernetNetworkUpdateRequest UPDATE_REQUEST = - new EthernetNetworkUpdateRequest.Builder() - .setIpConfiguration(new IpConfiguration()) - .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) - .build(); - private static final EthernetNetworkUpdateRequest UPDATE_REQUEST_WITHOUT_CAPABILITIES = - new EthernetNetworkUpdateRequest.Builder() - .setIpConfiguration(new IpConfiguration()) - .build(); - private static final EthernetNetworkUpdateRequest UPDATE_REQUEST_WITHOUT_IP_CONFIG = - new EthernetNetworkUpdateRequest.Builder() - .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) - .build(); - private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; - private EthernetServiceImpl mEthernetServiceImpl; - @Mock private Context mContext; - @Mock private Handler mHandler; - @Mock private EthernetTracker mEthernetTracker; - @Mock private PackageManager mPackageManager; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - doReturn(mPackageManager).when(mContext).getPackageManager(); - mEthernetServiceImpl = new EthernetServiceImpl(mContext, mHandler, mEthernetTracker); - mEthernetServiceImpl.mStarted.set(true); - toggleAutomotiveFeature(true); - shouldTrackIface(TEST_IFACE, true); - } - - private void toggleAutomotiveFeature(final boolean isEnabled) { - doReturn(isEnabled) - .when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); - } - - private void shouldTrackIface(@NonNull final String iface, final boolean shouldTrack) { - doReturn(shouldTrack).when(mEthernetTracker).isTrackingInterface(iface); - } - - @Test - public void testSetConfigurationRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.setConfiguration("" /* iface */, new IpConfiguration()); - }); - } - - @Test - public void testUpdateConfigurationRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.updateConfiguration( - "" /* iface */, UPDATE_REQUEST, null /* listener */); - }); - } - - @Test - public void testConnectNetworkRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.connectNetwork("" /* iface */, null /* listener */); - }); - } - - @Test - public void testDisconnectNetworkRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.disconnectNetwork("" /* iface */, null /* listener */); - }); - } - - @Test - public void testUpdateConfigurationRejectsNullIface() { - assertThrows(NullPointerException.class, () -> { - mEthernetServiceImpl.updateConfiguration(null, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testConnectNetworkRejectsNullIface() { - assertThrows(NullPointerException.class, () -> { - mEthernetServiceImpl.connectNetwork(null /* iface */, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsNullIface() { - assertThrows(NullPointerException.class, () -> { - mEthernetServiceImpl.disconnectNetwork(null /* iface */, NULL_LISTENER); - }); - } - - @Test - public void testUpdateConfigurationWithCapabilitiesRejectsWithoutAutomotiveFeature() { - toggleAutomotiveFeature(false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testUpdateConfigurationWithCapabilitiesWithAutomotiveFeature() { - toggleAutomotiveFeature(false); - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST_WITHOUT_CAPABILITIES, - NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), - eq(UPDATE_REQUEST_WITHOUT_CAPABILITIES.getIpConfiguration()), - eq(UPDATE_REQUEST_WITHOUT_CAPABILITIES.getNetworkCapabilities()), isNull()); - } - - @Test - public void testConnectNetworkRejectsWithoutAutomotiveFeature() { - toggleAutomotiveFeature(false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.connectNetwork("" /* iface */, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsWithoutAutomotiveFeature() { - toggleAutomotiveFeature(false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.disconnectNetwork("" /* iface */, NULL_LISTENER); - }); - } - - private void denyManageEthPermission() { - doThrow(new SecurityException("")).when(mContext) - .enforceCallingOrSelfPermission( - eq(Manifest.permission.MANAGE_ETHERNET_NETWORKS), anyString()); - } - - private void denyManageTestNetworksPermission() { - doThrow(new SecurityException("")).when(mContext) - .enforceCallingOrSelfPermission( - eq(Manifest.permission.MANAGE_TEST_NETWORKS), anyString()); - } - - @Test - public void testUpdateConfigurationRejectsWithoutManageEthPermission() { - denyManageEthPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testConnectNetworkRejectsWithoutManageEthPermission() { - denyManageEthPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsWithoutManageEthPermission() { - denyManageEthPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - private void enableTestInterface() { - when(mEthernetTracker.isValidTestInterface(eq(TEST_IFACE))).thenReturn(true); - } - - @Test - public void testUpdateConfigurationRejectsTestRequestWithoutTestPermission() { - enableTestInterface(); - denyManageTestNetworksPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testConnectNetworkRejectsTestRequestWithoutTestPermission() { - enableTestInterface(); - denyManageTestNetworksPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsTestRequestWithoutTestPermission() { - enableTestInterface(); - denyManageTestNetworksPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - @Test - public void testUpdateConfiguration() { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration( - eq(TEST_IFACE), - eq(UPDATE_REQUEST.getIpConfiguration()), - eq(UPDATE_REQUEST.getNetworkCapabilities()), eq(NULL_LISTENER)); - } - - @Test - public void testConnectNetwork() { - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).connectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - @Test - public void testDisconnectNetwork() { - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - @Test - public void testUpdateConfigurationAcceptsTestRequestWithNullCapabilities() { - enableTestInterface(); - final EthernetNetworkUpdateRequest request = - new EthernetNetworkUpdateRequest - .Builder() - .setIpConfiguration(new IpConfiguration()).build(); - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), - eq(request.getIpConfiguration()), - eq(request.getNetworkCapabilities()), isNull()); - } - - @Test - public void testUpdateConfigurationAcceptsRequestWithNullIpConfiguration() { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST_WITHOUT_IP_CONFIG, - NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), - eq(UPDATE_REQUEST_WITHOUT_IP_CONFIG.getIpConfiguration()), - eq(UPDATE_REQUEST_WITHOUT_IP_CONFIG.getNetworkCapabilities()), isNull()); - } - - @Test - public void testUpdateConfigurationRejectsInvalidTestRequest() { - enableTestInterface(); - assertThrows(IllegalArgumentException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - private EthernetNetworkUpdateRequest createTestNetworkUpdateRequest() { - final NetworkCapabilities nc = new NetworkCapabilities - .Builder(UPDATE_REQUEST.getNetworkCapabilities()) - .addTransportType(TRANSPORT_TEST).build(); - - return new EthernetNetworkUpdateRequest - .Builder(UPDATE_REQUEST) - .setNetworkCapabilities(nc).build(); - } - - @Test - public void testUpdateConfigurationForTestRequestDoesNotRequireAutoOrEthernetPermission() { - enableTestInterface(); - toggleAutomotiveFeature(false); - denyManageEthPermission(); - final EthernetNetworkUpdateRequest request = createTestNetworkUpdateRequest(); - - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration( - eq(TEST_IFACE), - eq(request.getIpConfiguration()), - eq(request.getNetworkCapabilities()), eq(NULL_LISTENER)); - } - - @Test - public void testConnectNetworkForTestRequestDoesNotRequireAutoOrNetPermission() { - enableTestInterface(); - toggleAutomotiveFeature(false); - denyManageEthPermission(); - - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).connectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - @Test - public void testDisconnectNetworkForTestRequestDoesNotRequireAutoOrNetPermission() { - enableTestInterface(); - toggleAutomotiveFeature(false); - denyManageEthPermission(); - - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - private void denyPermissions(String... permissions) { - for (String permission: permissions) { - doReturn(PackageManager.PERMISSION_DENIED).when(mContext) - .checkCallingOrSelfPermission(eq(permission)); - } - } - - @Test - public void testSetEthernetEnabled() { - denyPermissions(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); - mEthernetServiceImpl.setEthernetEnabled(true); - verify(mEthernetTracker).setEthernetEnabled(true); - reset(mEthernetTracker); - - denyPermissions(Manifest.permission.NETWORK_STACK); - mEthernetServiceImpl.setEthernetEnabled(false); - verify(mEthernetTracker).setEthernetEnabled(false); - reset(mEthernetTracker); - - denyPermissions(Manifest.permission.NETWORK_SETTINGS); - try { - mEthernetServiceImpl.setEthernetEnabled(true); - fail("Should get SecurityException"); - } catch (SecurityException e) { } - verify(mEthernetTracker, never()).setEthernetEnabled(false); - } -} diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java deleted file mode 100644 index b1831c4..0000000 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ /dev/null @@ -1,456 +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.TestNetworkManager.TEST_TAP_PREFIX; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.res.Resources; -import android.net.EthernetManager; -import android.net.InetAddresses; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.IEthernetServiceListener; -import android.net.INetd; -import android.net.IpConfiguration; -import android.net.IpConfiguration.IpAssignment; -import android.net.IpConfiguration.ProxySettings; -import android.net.InterfaceConfigurationParcel; -import android.net.LinkAddress; -import android.net.NetworkCapabilities; -import android.net.StaticIpConfiguration; -import android.os.HandlerThread; -import android.os.RemoteException; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.connectivity.resources.R; -import com.android.testutils.HandlerUtils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.InetAddress; -import java.util.ArrayList; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class EthernetTrackerTest { - private static final String TEST_IFACE = "test123"; - private static final int TIMEOUT_MS = 1_000; - private static final String THREAD_NAME = "EthernetServiceThread"; - private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; - private EthernetTracker tracker; - private HandlerThread mHandlerThread; - @Mock private Context mContext; - @Mock private EthernetNetworkFactory mFactory; - @Mock private INetd mNetd; - @Mock private EthernetTracker.Dependencies mDeps; - - @Before - public void setUp() throws RemoteException { - MockitoAnnotations.initMocks(this); - initMockResources(); - when(mFactory.updateInterfaceLinkState(anyString(), anyBoolean(), any())).thenReturn(false); - when(mNetd.interfaceGetList()).thenReturn(new String[0]); - mHandlerThread = new HandlerThread(THREAD_NAME); - mHandlerThread.start(); - tracker = new EthernetTracker(mContext, mHandlerThread.getThreadHandler(), mFactory, mNetd, - mDeps); - } - - @After - public void cleanUp() { - mHandlerThread.quitSafely(); - } - - private void initMockResources() { - when(mDeps.getInterfaceRegexFromResource(eq(mContext))).thenReturn(""); - when(mDeps.getInterfaceConfigFromResource(eq(mContext))).thenReturn(new String[0]); - } - - private void waitForIdle() { - HandlerUtils.waitForIdle(mHandlerThread, TIMEOUT_MS); - } - - /** - * Test: Creation of various valid static IP configurations - */ - @Test - public void createStaticIpConfiguration() { - // Empty gives default StaticIPConfiguration object - assertStaticConfiguration(new StaticIpConfiguration(), ""); - - // Setting only the IP address properly cascades and assumes defaults - assertStaticConfiguration(new StaticIpConfiguration.Builder() - .setIpAddress(new LinkAddress("192.0.2.10/24")).build(), "ip=192.0.2.10/24"); - - final ArrayList dnsAddresses = new ArrayList<>(); - dnsAddresses.add(InetAddresses.parseNumericAddress("4.4.4.4")); - dnsAddresses.add(InetAddresses.parseNumericAddress("8.8.8.8")); - // Setting other fields properly cascades them - assertStaticConfiguration(new StaticIpConfiguration.Builder() - .setIpAddress(new LinkAddress("192.0.2.10/24")) - .setDnsServers(dnsAddresses) - .setGateway(InetAddresses.parseNumericAddress("192.0.2.1")) - .setDomains("android").build(), - "ip=192.0.2.10/24 dns=4.4.4.4,8.8.8.8 gateway=192.0.2.1 domains=android"); - - // Verify order doesn't matter - assertStaticConfiguration(new StaticIpConfiguration.Builder() - .setIpAddress(new LinkAddress("192.0.2.10/24")) - .setDnsServers(dnsAddresses) - .setGateway(InetAddresses.parseNumericAddress("192.0.2.1")) - .setDomains("android").build(), - "domains=android ip=192.0.2.10/24 gateway=192.0.2.1 dns=4.4.4.4,8.8.8.8 "); - } - - /** - * Test: Attempt creation of various bad static IP configurations - */ - @Test - public void createStaticIpConfiguration_Bad() { - assertStaticConfigurationFails("ip=192.0.2.1/24 gateway= blah=20.20.20.20"); // Unknown key - assertStaticConfigurationFails("ip=192.0.2.1"); // mask is missing - assertStaticConfigurationFails("ip=a.b.c"); // not a valid ip address - assertStaticConfigurationFails("dns=4.4.4.4,1.2.3.A"); // not valid ip address in dns - assertStaticConfigurationFails("="); // Key and value is empty - assertStaticConfigurationFails("ip="); // Value is empty - assertStaticConfigurationFails("ip=192.0.2.1/24 gateway="); // Gateway is empty - } - - private void assertStaticConfigurationFails(String config) { - try { - EthernetTracker.parseStaticIpConfiguration(config); - fail("Expected to fail: " + config); - } catch (IllegalArgumentException e) { - // expected - } - } - - private void assertStaticConfiguration(StaticIpConfiguration expectedStaticIpConfig, - String configAsString) { - final IpConfiguration expectedIpConfiguration = new IpConfiguration(); - expectedIpConfiguration.setIpAssignment(IpAssignment.STATIC); - expectedIpConfiguration.setProxySettings(ProxySettings.NONE); - expectedIpConfiguration.setStaticIpConfiguration(expectedStaticIpConfig); - - assertEquals(expectedIpConfiguration, - EthernetTracker.parseStaticIpConfiguration(configAsString)); - } - - private NetworkCapabilities.Builder makeEthernetCapabilitiesBuilder(boolean clearAll) { - final NetworkCapabilities.Builder builder = - clearAll ? NetworkCapabilities.Builder.withoutDefaultCapabilities() - : new NetworkCapabilities.Builder(); - return builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); - } - - /** - * Test: Attempt to create a capabilties with various valid sets of capabilities/transports - */ - @Test - public void createNetworkCapabilities() { - - // Particularly common expected results - NetworkCapabilities defaultEthernetCleared = - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .build(); - - NetworkCapabilities ethernetClearedWithCommonCaps = - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .addCapability(12) - .addCapability(13) - .addCapability(14) - .addCapability(15) - .build(); - - // Empty capabilities and transports lists with a "please clear defaults" should - // yield an empty capabilities set with TRANPORT_ETHERNET - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", ""); - - // Empty capabilities and transports without the clear defaults flag should return the - // default capabilities set with TRANSPORT_ETHERNET - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(false /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .build(), - false, "", ""); - - // A list of capabilities without the clear defaults flag should return the default - // capabilities, mixed with the desired capabilities, and TRANSPORT_ETHERNET - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(false /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .addCapability(11) - .addCapability(12) - .build(), - false, "11,12", ""); - - // Adding a list of capabilities with a clear defaults will leave exactly those capabilities - // with a default TRANSPORT_ETHERNET since no overrides are specified - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "12,13,14,15", ""); - - // Adding any invalid capabilities to the list will cause them to be ignored - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "12,13,14,15,65,73", ""); - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "12,13,14,15,abcdefg", ""); - - // Adding a valid override transport will remove the default TRANSPORT_ETHERNET transport - // and apply only the override to the capabiltities object - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(0) - .build(), - true, "", "0"); - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(1) - .build(), - true, "", "1"); - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(2) - .build(), - true, "", "2"); - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(3) - .build(), - true, "", "3"); - - // "4" is TRANSPORT_VPN, which is unsupported. Should default back to TRANPORT_ETHERNET - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "4"); - - // "5" is TRANSPORT_WIFI_AWARE, which is currently supported due to no legacy TYPE_NONE - // conversion. When that becomes available, this test must be updated - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "5"); - - // "6" is TRANSPORT_LOWPAN, which is currently supported due to no legacy TYPE_NONE - // conversion. When that becomes available, this test must be updated - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "6"); - - // Adding an invalid override transport will leave the transport as TRANSPORT_ETHERNET - assertParsedNetworkCapabilities(defaultEthernetCleared,true, "", "100"); - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "abcdefg"); - - // Ensure the adding of both capabilities and transports work - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addCapability(12) - .addCapability(13) - .addCapability(14) - .addCapability(15) - .addTransportType(3) - .build(), - true, "12,13,14,15", "3"); - - // Ensure order does not matter for capability list - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "13,12,15,14", ""); - } - - private void assertParsedNetworkCapabilities(NetworkCapabilities expectedNetworkCapabilities, - boolean clearCapabilties, String configCapabiltiies,String configTransports) { - assertEquals(expectedNetworkCapabilities, - EthernetTracker.createNetworkCapabilities(clearCapabilties, configCapabiltiies, - configTransports).build()); - } - - @Test - public void testCreateEthernetTrackerConfigReturnsCorrectValue() { - final String capabilities = "2"; - final String ipConfig = "3"; - final String transport = "4"; - final String configString = String.join(";", TEST_IFACE, capabilities, ipConfig, transport); - - final EthernetTracker.EthernetTrackerConfig config = - EthernetTracker.createEthernetTrackerConfig(configString); - - assertEquals(TEST_IFACE, config.mIface); - assertEquals(capabilities, config.mCapabilities); - assertEquals(ipConfig, config.mIpConfig); - assertEquals(transport, config.mTransport); - } - - @Test - public void testCreateEthernetTrackerConfigThrowsNpeWithNullInput() { - assertThrows(NullPointerException.class, - () -> EthernetTracker.createEthernetTrackerConfig(null)); - } - - @Test - public void testUpdateConfiguration() { - final NetworkCapabilities capabilities = new NetworkCapabilities.Builder().build(); - final LinkAddress linkAddr = new LinkAddress("192.0.2.2/25"); - final StaticIpConfiguration staticIpConfig = - new StaticIpConfiguration.Builder().setIpAddress(linkAddr).build(); - final IpConfiguration ipConfig = - new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); - final INetworkInterfaceOutcomeReceiver listener = null; - - tracker.updateConfiguration(TEST_IFACE, ipConfig, capabilities, listener); - waitForIdle(); - - verify(mFactory).updateInterface( - eq(TEST_IFACE), eq(ipConfig), eq(capabilities), eq(listener)); - } - - @Test - public void testConnectNetworkCorrectlyCallsFactory() { - tracker.connectNetwork(TEST_IFACE, NULL_LISTENER); - waitForIdle(); - - verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(true /* up */), - eq(NULL_LISTENER)); - } - - @Test - public void testDisconnectNetworkCorrectlyCallsFactory() { - tracker.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - waitForIdle(); - - verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(false /* up */), - eq(NULL_LISTENER)); - } - - @Test - public void testIsValidTestInterfaceIsFalseWhenTestInterfacesAreNotIncluded() { - final String validIfaceName = TEST_TAP_PREFIX + "123"; - tracker.setIncludeTestInterfaces(false); - waitForIdle(); - - final boolean isValidTestInterface = tracker.isValidTestInterface(validIfaceName); - - assertFalse(isValidTestInterface); - } - - @Test - public void testIsValidTestInterfaceIsFalseWhenTestInterfaceNameIsInvalid() { - final String invalidIfaceName = "123" + TEST_TAP_PREFIX; - tracker.setIncludeTestInterfaces(true); - waitForIdle(); - - final boolean isValidTestInterface = tracker.isValidTestInterface(invalidIfaceName); - - assertFalse(isValidTestInterface); - } - - @Test - public void testIsValidTestInterfaceIsTrueWhenTestInterfacesIncludedAndValidName() { - final String validIfaceName = TEST_TAP_PREFIX + "123"; - tracker.setIncludeTestInterfaces(true); - waitForIdle(); - - final boolean isValidTestInterface = tracker.isValidTestInterface(validIfaceName); - - assertTrue(isValidTestInterface); - } - - public static class EthernetStateListener extends IEthernetServiceListener.Stub { - @Override - public void onEthernetStateChanged(int state) { } - - @Override - public void onInterfaceStateChanged(String iface, int state, int role, - IpConfiguration configuration) { } - } - - @Test - public void testListenEthernetStateChange() throws Exception { - final String testIface = "testtap123"; - final String testHwAddr = "11:22:33:44:55:66"; - final InterfaceConfigurationParcel ifaceParcel = new InterfaceConfigurationParcel(); - ifaceParcel.ifName = testIface; - ifaceParcel.hwAddr = testHwAddr; - ifaceParcel.flags = new String[] {INetd.IF_STATE_UP}; - - tracker.setIncludeTestInterfaces(true); - waitForIdle(); - - when(mNetd.interfaceGetList()).thenReturn(new String[] {testIface}); - when(mNetd.interfaceGetCfg(eq(testIface))).thenReturn(ifaceParcel); - doReturn(new String[] {testIface}).when(mFactory).getAvailableInterfaces(anyBoolean()); - doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface)); - - final EthernetStateListener listener = spy(new EthernetStateListener()); - tracker.addListener(listener, true /* canUseRestrictedNetworks */); - // Check default state. - waitForIdle(); - verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP), - anyInt(), any()); - verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED)); - reset(listener); - - doReturn(EthernetManager.STATE_ABSENT).when(mFactory).getInterfaceState(eq(testIface)); - tracker.setEthernetEnabled(false); - waitForIdle(); - verify(mFactory).removeInterface(eq(testIface)); - verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_DISABLED)); - verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_ABSENT), - anyInt(), any()); - reset(listener); - - doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface)); - tracker.setEthernetEnabled(true); - waitForIdle(); - verify(mFactory).addInterface(eq(testIface), eq(testHwAddr), any(), any()); - verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED)); - verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP), - anyInt(), any()); - } -} -- cgit v1.2.3