blob: 9987b3e00c09ad43bd5f5fdf0c64968fa5e88d2b [file] [log] [blame]
/*
* 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.IEthernetNetworkManagementListener;
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.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 logIfEthernetNotStarted() {
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) {
logIfEthernetNotStarted();
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");
}
/**
* Validate the state of ethernet for APIs tied to network management.
*
* @param iface the ethernet interface name to operate on.
* @param methodName the name of the calling method.
*/
private void validateNetworkManagementState(@NonNull final String iface,
final @NonNull String methodName) {
Objects.requireNonNull(iface, "Pass a non-null iface.");
Objects.requireNonNull(methodName, "Pass a non-null methodName.");
// Only bypass the permission/device checks if this is a valid test interface.
if (mTracker.isValidTestInterface(iface)) {
enforceManageTestNetworksPermission();
Log.i(TAG, "Ethernet network management API used with test interface " + iface);
} else {
enforceAutomotiveDevice(methodName);
enforceNetworkManagementPermission();
}
logIfEthernetNotStarted();
}
private void validateTestCapabilities(@Nullable final NetworkCapabilities nc) {
if (null != nc && nc.hasTransport(TRANSPORT_TEST)) {
return;
}
throw new IllegalArgumentException(
"Updates to test interfaces must have NetworkCapabilities.TRANSPORT_TEST.");
}
@Override
public void updateConfiguration(@NonNull final String iface,
@NonNull final EthernetNetworkUpdateRequest request,
@Nullable final IEthernetNetworkManagementListener listener) {
validateNetworkManagementState(iface, "updateConfiguration()");
if (mTracker.isValidTestInterface(iface)) {
validateTestCapabilities(request.getNetworkCapabilities());
// TODO: use NetworkCapabilities#restrictCapabilitiesForTestNetwork when available on a
// local NetworkCapabilities copy to pass to mTracker.updateConfiguration.
}
// TODO: validate that iface is listed in overlay config_ethernet_interfaces
mTracker.updateConfiguration(
iface, request.getIpConfiguration(), request.getNetworkCapabilities(), listener);
}
@Override
public void connectNetwork(@NonNull final String iface,
@Nullable final IEthernetNetworkManagementListener listener) {
Log.i(TAG, "connectNetwork called with: iface=" + iface + ", listener=" + listener);
validateNetworkManagementState(iface, "connectNetwork()");
mTracker.connectNetwork(iface, listener);
}
@Override
public void disconnectNetwork(@NonNull final String iface,
@Nullable final IEthernetNetworkManagementListener listener) {
Log.i(TAG, "disconnectNetwork called with: iface=" + iface + ", listener=" + listener);
validateNetworkManagementState(iface, "disconnectNetwork()");
mTracker.disconnectNetwork(iface, listener);
}
}