/* * 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 android.net; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; import android.os.OutcomeReceiver; import android.os.RemoteException; import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.modules.utils.BackgroundThread; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.IntConsumer; /** * A class that manages and configures Ethernet interfaces. * * @hide */ @SystemApi @SystemService(Context.ETHERNET_SERVICE) public class EthernetManager { private static final String TAG = "EthernetManager"; private final IEthernetManager mService; @GuardedBy("mListenerLock") private final ArrayMap mIfaceServiceListeners = new ArrayMap<>(); @GuardedBy("mListenerLock") private final ArrayMap mStateServiceListeners = new ArrayMap<>(); final Object mListenerLock = new Object(); /** * Indicates that Ethernet is disabled. * * @hide */ @SystemApi(client = MODULE_LIBRARIES) public static final int ETHERNET_STATE_DISABLED = 0; /** * Indicates that Ethernet is enabled. * * @hide */ @SystemApi(client = MODULE_LIBRARIES) public static final int ETHERNET_STATE_ENABLED = 1; /** * The interface is absent. * @hide */ @SystemApi(client = MODULE_LIBRARIES) public static final int STATE_ABSENT = 0; /** * The interface is present but link is down. * @hide */ @SystemApi(client = MODULE_LIBRARIES) public static final int STATE_LINK_DOWN = 1; /** * The interface is present and link is up. * @hide */ @SystemApi(client = MODULE_LIBRARIES) public static final int STATE_LINK_UP = 2; /** @hide */ @IntDef(prefix = "STATE_", value = {STATE_ABSENT, STATE_LINK_DOWN, STATE_LINK_UP}) @Retention(RetentionPolicy.SOURCE) public @interface InterfaceState {} /** * The interface currently does not have any specific role. * @hide */ @SystemApi(client = MODULE_LIBRARIES) public static final int ROLE_NONE = 0; /** * The interface is in client mode (e.g., connected to the Internet). * @hide */ @SystemApi(client = MODULE_LIBRARIES) public static final int ROLE_CLIENT = 1; /** * Ethernet interface is in server mode (e.g., providing Internet access to tethered devices). * @hide */ @SystemApi(client = MODULE_LIBRARIES) public static final int ROLE_SERVER = 2; /** @hide */ @IntDef(prefix = "ROLE_", value = {ROLE_NONE, ROLE_CLIENT, ROLE_SERVER}) @Retention(RetentionPolicy.SOURCE) public @interface Role {} /** * A listener that receives notifications about the state of Ethernet interfaces on the system. * @hide */ @SystemApi(client = MODULE_LIBRARIES) public interface InterfaceStateListener { /** * Called when an Ethernet interface changes state. * * @param iface the name of the interface. * @param state the current state of the interface, or {@link #STATE_ABSENT} if the * interface was removed. * @param role whether the interface is in client mode or server mode. * @param configuration the current IP configuration of the interface. * @hide */ @SystemApi(client = MODULE_LIBRARIES) void onInterfaceStateChanged(@NonNull String iface, @InterfaceState int state, @Role int role, @Nullable IpConfiguration configuration); } /** * A listener interface to receive notification on changes in Ethernet. * This has never been a supported API. Use {@link InterfaceStateListener} instead. * @hide */ public interface Listener extends InterfaceStateListener { /** * Called when Ethernet port's availability is changed. * @param iface Ethernet interface name * @param isAvailable {@code true} if Ethernet port exists. * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) void onAvailabilityChanged(String iface, boolean isAvailable); /** Default implementation for backwards compatibility. Only calls the legacy listener. */ default void onInterfaceStateChanged(@NonNull String iface, @InterfaceState int state, @Role int role, @Nullable IpConfiguration configuration) { onAvailabilityChanged(iface, (state >= STATE_LINK_UP)); } } /** * Create a new EthernetManager instance. * Applications will almost always want to use * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve * the standard {@link android.content.Context#ETHERNET_SERVICE Context.ETHERNET_SERVICE}. * @hide */ public EthernetManager(Context context, IEthernetManager service) { mService = service; } /** * Get Ethernet configuration. * @return the Ethernet Configuration, contained in {@link IpConfiguration}. * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public IpConfiguration getConfiguration(String iface) { try { return mService.getConfiguration(iface); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Set Ethernet configuration. * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setConfiguration(@NonNull String iface, @NonNull IpConfiguration config) { try { mService.setConfiguration(iface, config); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Indicates whether the system currently has one or more Ethernet interfaces. * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isAvailable() { return getAvailableInterfaces().length > 0; } /** * Indicates whether the system has given interface. * * @param iface Ethernet interface name * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isAvailable(String iface) { try { return mService.isAvailable(iface); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Adds a listener. * This has never been a supported API. Use {@link #addInterfaceStateListener} instead. * * @param listener A {@link Listener} to add. * @throws IllegalArgumentException If the listener is null. * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void addListener(@NonNull Listener listener) { addListener(listener, BackgroundThread.getExecutor()); } /** * Adds a listener. * This has never been a supported API. Use {@link #addInterfaceStateListener} instead. * * @param listener A {@link Listener} to add. * @param executor Executor to run callbacks on. * @throws IllegalArgumentException If the listener or executor is null. * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void addListener(@NonNull Listener listener, @NonNull Executor executor) { addInterfaceStateListener(executor, listener); } /** * Listen to changes in the state of Ethernet interfaces. * * Adds a listener to receive notification for any state change of all existing Ethernet * interfaces. *

{@link Listener#onInterfaceStateChanged} will be triggered immediately for all * existing interfaces upon adding a listener. The same method will be called on the * listener every time any of the interface changes state. In particular, if an * interface is removed, it will be called with state {@link #STATE_ABSENT}. *

Use {@link #removeInterfaceStateListener} with the same object to stop listening. * * @param executor Executor to run callbacks on. * @param listener A {@link Listener} to add. * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @SystemApi(client = MODULE_LIBRARIES) public void addInterfaceStateListener(@NonNull Executor executor, @NonNull InterfaceStateListener listener) { if (listener == null || executor == null) { throw new NullPointerException("listener and executor must not be null"); } final IEthernetServiceListener.Stub serviceListener = new IEthernetServiceListener.Stub() { @Override public void onEthernetStateChanged(int state) {} @Override public void onInterfaceStateChanged(String iface, int state, int role, IpConfiguration configuration) { executor.execute(() -> listener.onInterfaceStateChanged(iface, state, role, configuration)); } }; synchronized (mListenerLock) { addServiceListener(serviceListener); mIfaceServiceListeners.put(listener, serviceListener); } } @GuardedBy("mListenerLock") private void addServiceListener(@NonNull final IEthernetServiceListener listener) { try { mService.addListener(listener); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns an array of available Ethernet interface names. * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public String[] getAvailableInterfaces() { try { return mService.getAvailableInterfaces(); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Removes a listener. * * @param listener A {@link Listener} to remove. * @hide */ @SystemApi(client = MODULE_LIBRARIES) public void removeInterfaceStateListener(@NonNull InterfaceStateListener listener) { Objects.requireNonNull(listener); synchronized (mListenerLock) { maybeRemoveServiceListener(mIfaceServiceListeners.remove(listener)); } } @GuardedBy("mListenerLock") private void maybeRemoveServiceListener(@Nullable final IEthernetServiceListener listener) { if (listener == null) return; try { mService.removeListener(listener); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Removes a listener. * This has never been a supported API. Use {@link #removeInterfaceStateListener} instead. * @param listener A {@link Listener} to remove. * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void removeListener(@NonNull Listener listener) { if (listener == null) { throw new IllegalArgumentException("listener must not be null"); } removeInterfaceStateListener(listener); } /** * Whether to treat interfaces created by {@link TestNetworkManager#createTapInterface} * as Ethernet interfaces. The effects of this method apply to any test interfaces that are * already present on the system. * @hide */ @SystemApi(client = MODULE_LIBRARIES) public void setIncludeTestInterfaces(boolean include) { try { mService.setIncludeTestInterfaces(include); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * A request for a tethered interface. */ public static class TetheredInterfaceRequest { private final IEthernetManager mService; private final ITetheredInterfaceCallback mCb; private TetheredInterfaceRequest(@NonNull IEthernetManager service, @NonNull ITetheredInterfaceCallback cb) { this.mService = service; this.mCb = cb; } /** * Release the request, causing the interface to revert back from tethering mode if there * is no other requestor. */ public void release() { try { mService.releaseTetheredInterface(mCb); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } } /** * Callback for {@link #requestTetheredInterface(TetheredInterfaceCallback)}. */ public interface TetheredInterfaceCallback { /** * Called when the tethered interface is available. * @param iface The name of the interface. */ void onAvailable(@NonNull String iface); /** * Called when the tethered interface is now unavailable. */ void onUnavailable(); } /** * Request a tethered interface in tethering mode. * *

When this method is called and there is at least one ethernet interface available, the * system will designate one to act as a tethered interface. If there is already a tethered * interface, the existing interface will be used. * @param callback A callback to be called once the request has been fulfilled. */ @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK }) @NonNull public TetheredInterfaceRequest requestTetheredInterface(@NonNull final Executor executor, @NonNull final TetheredInterfaceCallback callback) { Objects.requireNonNull(callback, "Callback must be non-null"); Objects.requireNonNull(executor, "Executor must be non-null"); final ITetheredInterfaceCallback cbInternal = new ITetheredInterfaceCallback.Stub() { @Override public void onAvailable(String iface) { executor.execute(() -> callback.onAvailable(iface)); } @Override public void onUnavailable() { executor.execute(() -> callback.onUnavailable()); } }; try { mService.requestTetheredInterface(cbInternal); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return new TetheredInterfaceRequest(mService, cbInternal); } private static final class NetworkInterfaceOutcomeReceiver extends INetworkInterfaceOutcomeReceiver.Stub { @NonNull private final Executor mExecutor; @NonNull private final OutcomeReceiver mCallback; NetworkInterfaceOutcomeReceiver( @NonNull final Executor executor, @NonNull final OutcomeReceiver callback) { Objects.requireNonNull(executor, "Pass a non-null executor"); Objects.requireNonNull(callback, "Pass a non-null callback"); mExecutor = executor; mCallback = callback; } @Override public void onResult(@NonNull String iface) { mExecutor.execute(() -> mCallback.onResult(iface)); } @Override public void onError(@NonNull EthernetNetworkManagementException e) { mExecutor.execute(() -> mCallback.onError(e)); } } private NetworkInterfaceOutcomeReceiver makeNetworkInterfaceOutcomeReceiver( @Nullable final Executor executor, @Nullable final OutcomeReceiver callback) { if (null != callback) { Objects.requireNonNull(executor, "Pass a non-null executor, or a null callback"); } final NetworkInterfaceOutcomeReceiver proxy; if (null == callback) { proxy = null; } else { proxy = new NetworkInterfaceOutcomeReceiver(executor, callback); } return proxy; } /** * Updates the configuration of an automotive device's ethernet network. * * The {@link EthernetNetworkUpdateRequest} {@code request} argument describes how to update the * configuration for this network. * Use {@link StaticIpConfiguration.Builder} to build a {@code StaticIpConfiguration} object for * this network to put inside the {@code request}. * Similarly, use {@link NetworkCapabilities.Builder} to build a {@code NetworkCapabilities} * object for this network to put inside the {@code request}. * * The provided {@link OutcomeReceiver} is called once the operation has finished execution. * * @param iface the name of the interface to act upon. * @param request the {@link EthernetNetworkUpdateRequest} used to set an ethernet network's * {@link StaticIpConfiguration} and {@link NetworkCapabilities} values. * @param executor an {@link Executor} to execute the callback on. Optional if callback is null. * @param callback an optional {@link OutcomeReceiver} to listen for completion of the * operation. On success, {@link OutcomeReceiver#onResult} is called with the * interface name. On error, {@link OutcomeReceiver#onError} is called with more * information about the error. * @throws SecurityException if the process doesn't hold * {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}. * @throws UnsupportedOperationException if the {@link NetworkCapabilities} are updated on a * non-automotive device or this function is called on an * unsupported interface. * @hide */ @SystemApi @RequiresPermission(anyOf = { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void updateConfiguration( @NonNull String iface, @NonNull EthernetNetworkUpdateRequest request, @Nullable @CallbackExecutor Executor executor, @Nullable OutcomeReceiver callback) { Objects.requireNonNull(iface, "iface must be non-null"); Objects.requireNonNull(request, "request must be non-null"); final NetworkInterfaceOutcomeReceiver proxy = makeNetworkInterfaceOutcomeReceiver( executor, callback); try { mService.updateConfiguration(iface, request, proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Enable a network interface. * * Enables a previously disabled network interface. An attempt to enable an already-enabled * interface is ignored. * The provided {@link OutcomeReceiver} is called once the operation has finished execution. * * @param iface the name of the interface to enable. * @param executor an {@link Executor} to execute the callback on. Optional if callback is null. * @param callback an optional {@link OutcomeReceiver} to listen for completion of the * operation. On success, {@link OutcomeReceiver#onResult} is called with the * interface name. On error, {@link OutcomeReceiver#onError} is called with more * information about the error. * @throws SecurityException if the process doesn't hold * {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}. * @hide */ @SystemApi @RequiresPermission(anyOf = { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void enableInterface( @NonNull String iface, @Nullable @CallbackExecutor Executor executor, @Nullable OutcomeReceiver callback) { Objects.requireNonNull(iface, "iface must be non-null"); final NetworkInterfaceOutcomeReceiver proxy = makeNetworkInterfaceOutcomeReceiver( executor, callback); try { mService.enableInterface(iface, proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Disable a network interface. * * Disables the specified interface. If this interface is in use in a connected * {@link android.net.Network}, then that {@code Network} will be torn down. * The provided {@link OutcomeReceiver} is called once the operation has finished execution. * * @param iface the name of the interface to disable. * @param executor an {@link Executor} to execute the callback on. Optional if callback is null. * @param callback an optional {@link OutcomeReceiver} to listen for completion of the * operation. On success, {@link OutcomeReceiver#onResult} is called with the * interface name. On error, {@link OutcomeReceiver#onError} is called with more * information about the error. * @throws SecurityException if the process doesn't hold * {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}. * @hide */ @SystemApi @RequiresPermission(anyOf = { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void disableInterface( @NonNull String iface, @Nullable @CallbackExecutor Executor executor, @Nullable OutcomeReceiver callback) { Objects.requireNonNull(iface, "iface must be non-null"); final NetworkInterfaceOutcomeReceiver proxy = makeNetworkInterfaceOutcomeReceiver( executor, callback); try { mService.disableInterface(iface, proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Change ethernet setting. * * @param enabled enable or disable ethernet settings. * * @hide */ @RequiresPermission(anyOf = { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) @SystemApi(client = MODULE_LIBRARIES) public void setEthernetEnabled(boolean enabled) { try { mService.setEthernetEnabled(enabled); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Listen to changes in the state of ethernet. * * @param executor to run callbacks on. * @param listener to listen ethernet state changed. * * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @SystemApi(client = MODULE_LIBRARIES) public void addEthernetStateListener(@NonNull Executor executor, @NonNull IntConsumer listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); final IEthernetServiceListener.Stub serviceListener = new IEthernetServiceListener.Stub() { @Override public void onEthernetStateChanged(int state) { executor.execute(() -> listener.accept(state)); } @Override public void onInterfaceStateChanged(String iface, int state, int role, IpConfiguration configuration) {} }; synchronized (mListenerLock) { addServiceListener(serviceListener); mStateServiceListeners.put(listener, serviceListener); } } /** * Removes a listener. * * @param listener to listen ethernet state changed. * * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @SystemApi(client = MODULE_LIBRARIES) public void removeEthernetStateListener(@NonNull IntConsumer listener) { Objects.requireNonNull(listener); synchronized (mListenerLock) { maybeRemoveServiceListener(mStateServiceListeners.remove(listener)); } } /** * Returns an array of existing Ethernet interface names regardless whether the interface * is available or not currently. * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @SystemApi(client = MODULE_LIBRARIES) @NonNull public List getInterfaceList() { try { return mService.getInterfaceList(); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } }