summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2020-01-24 17:57:27 +0900
committerRemi NGUYEN VAN <reminv@google.com>2020-01-28 16:22:46 +0900
commit1abc2d38825949004bb528e9a4634f931a7ba327 (patch)
tree668b66306f3b50b6e0845f7531fe464b4fcacc42
parent2e9854c9a8fecba3f91c544156e9b2e3d68cf01e (diff)
downloadethernet-1abc2d38825949004bb528e9a4634f931a7ba327.tar.gz
Support one Ethernet interface in server mode.
Currently, Ethernet only supports interfaces in client mode (e.g., to connect to the Internet). Add minimal support to Ethernet to support interfaces in server mode. This simple implementation only works on the default interface, which is the first interface that is detected on boot (even if the interface is later removed). This also provides (but does not yet unhide) a simple API for Tethering to request that the next-plugged-in interface to be placed into server mode. Test: Enabling tethering with change on top Bug: 130840861 Merged-In: I78f85bc36aaceb62ce274003a75ea99d0b6bc8b7 Change-Id: I78f85bc36aaceb62ce274003a75ea99d0b6bc8b7 (clean cherry-pick from internal branch)
-rw-r--r--java/com/android/server/ethernet/EthernetServiceImpl.java14
-rw-r--r--java/com/android/server/ethernet/EthernetTracker.java133
2 files changed, 138 insertions, 9 deletions
diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java
index fda3c3c..1cf38b4 100644
--- a/java/com/android/server/ethernet/EthernetServiceImpl.java
+++ b/java/com/android/server/ethernet/EthernetServiceImpl.java
@@ -20,12 +20,14 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.net.IEthernetManager;
import android.net.IEthernetServiceListener;
+import android.net.ITetheredInterfaceCallback;
import android.net.IpConfiguration;
import android.net.NetworkStack;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.RemoteException;
+import android.provider.Settings;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -162,6 +164,18 @@ public class EthernetServiceImpl extends IEthernetManager.Stub {
}
@Override
+ public void requestTetheredInterface(ITetheredInterfaceCallback callback) {
+ NetworkStack.checkNetworkStackPermission(mContext);
+ mTracker.requestTetheredInterface(callback);
+ }
+
+ @Override
+ public void releaseTetheredInterface(ITetheredInterfaceCallback callback) {
+ NetworkStack.checkNetworkStackPermission(mContext);
+ 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)
diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java
index 4ab1dff..24616b7 100644
--- a/java/com/android/server/ethernet/EthernetTracker.java
+++ b/java/com/android/server/ethernet/EthernetTracker.java
@@ -19,6 +19,7 @@ package com.android.server.ethernet;
import android.annotation.Nullable;
import android.content.Context;
import android.net.IEthernetServiceListener;
+import android.net.ITetheredInterfaceCallback;
import android.net.InterfaceConfiguration;
import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
@@ -61,6 +62,9 @@ import java.util.concurrent.ConcurrentHashMap;
* <p>All public or package private methods must be thread-safe unless stated otherwise.
*/
final class EthernetTracker {
+ private static final int INTERFACE_MODE_CLIENT = 1;
+ private static final int INTERFACE_MODE_SERVER = 2;
+
private final static String TAG = EthernetTracker.class.getSimpleName();
private final static boolean DBG = EthernetNetworkFactory.DBG;
@@ -73,6 +77,7 @@ final class EthernetTracker {
private final ConcurrentHashMap<String, IpConfiguration> mIpConfigurations =
new ConcurrentHashMap<>();
+ private final Context mContext;
private final INetworkManagementService mNMService;
private final Handler mHandler;
private final EthernetNetworkFactory mFactory;
@@ -80,10 +85,25 @@ final class EthernetTracker {
private final RemoteCallbackList<IEthernetServiceListener> mListeners =
new RemoteCallbackList<>();
-
+ private final TetheredInterfaceRequestList mTetheredInterfaceRequests =
+ new TetheredInterfaceRequestList();
+
+ // Used only on the handler thread
+ private String mDefaultInterface;
+ private int mDefaultInterfaceMode;
+ // Tracks whether clients were notified that the tethered interface is available
+ private boolean mTetheredInterfaceWasAvailable = false;
private volatile IpConfiguration mIpConfigForDefaultInterface;
+ private class TetheredInterfaceRequestList extends RemoteCallbackList<ITetheredInterfaceCallback> {
+ @Override
+ public void onCallbackDied(ITetheredInterfaceCallback cb, Object cookie) {
+ mHandler.post(EthernetTracker.this::maybeUntetherDefaultInterface);
+ }
+ }
+
EthernetTracker(Context context, Handler handler) {
+ mContext = context;
mHandler = handler;
// The services we use.
@@ -167,6 +187,68 @@ final class EthernetTracker {
mListeners.unregister(listener);
}
+ 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;
+ // mDefaultInterface null means that there never was a default interface (it is never set
+ // to null).
+ if (mDefaultInterfaceMode == INTERFACE_MODE_CLIENT || mDefaultInterface == null) return;
+
+ setDefaultInterfaceMode(INTERFACE_MODE_CLIENT);
+ }
+
+ private void setDefaultInterfaceMode(int mode) {
+ mDefaultInterfaceMode = mode;
+ removeInterface(mDefaultInterface);
+ addInterface(mDefaultInterface);
+ }
+
+ private int getInterfaceMode(final String iface) {
+ if (iface.equals(mDefaultInterface)) {
+ return mDefaultInterfaceMode;
+ }
+ return INTERFACE_MODE_CLIENT;
+ }
+
private void removeInterface(String iface) {
mFactory.removeInterface(iface);
}
@@ -198,13 +280,17 @@ final class EthernetTracker {
nc = createDefaultNetworkCapabilities();
}
}
- IpConfiguration ipConfiguration = mIpConfigurations.get(iface);
- if (ipConfiguration == null) {
- ipConfiguration = createDefaultIpConfiguration();
- }
- Log.d(TAG, "Started tracking interface " + iface);
- mFactory.addInterface(iface, hwAddress, nc, ipConfiguration);
+ final int mode = getInterfaceMode(iface);
+ if (mode == INTERFACE_MODE_CLIENT) {
+ IpConfiguration ipConfiguration = mIpConfigurations.get(iface);
+ if (ipConfiguration == null) {
+ ipConfiguration = createDefaultIpConfiguration();
+ }
+
+ Log.d(TAG, "Started tracking interface " + iface);
+ mFactory.addInterface(iface, hwAddress, nc, ipConfiguration);
+ }
// 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
@@ -215,8 +301,11 @@ final class EthernetTracker {
}
private void updateInterfaceState(String iface, boolean up) {
- boolean modified = mFactory.updateInterfaceLinkState(iface, up);
- if (modified) {
+ final int mode = getInterfaceMode(iface);
+ final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT)
+ && mFactory.updateInterfaceLinkState(iface, up);
+
+ if (factoryLinkStateUpdated) {
boolean restricted = isRestrictedInterface(iface);
int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
@@ -234,6 +323,25 @@ final class EthernetTracker {
}
mListeners.finishBroadcast();
}
+
+ updateServerModeInterfaceState(iface, up, mode);
+ }
+
+ private void updateServerModeInterfaceState(String iface, boolean up, int mode) {
+ final boolean available = up && (mode == INTERFACE_MODE_SERVER);
+ if (available == mTetheredInterfaceWasAvailable || !iface.equals(mDefaultInterface)) return;
+
+ 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) {
@@ -244,6 +352,11 @@ final class EthernetTracker {
return;
}
+ // TODO: avoid making an interface default if it has configured NetworkCapabilities.
+ if (mDefaultInterface == null) {
+ mDefaultInterface = iface;
+ }
+
if (mIpConfigForDefaultInterface != null) {
updateIpConfiguration(iface, mIpConfigForDefaultInterface);
mIpConfigForDefaultInterface = null;
@@ -467,6 +580,8 @@ final class EthernetTracker {
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("Listeners: " + mListeners.getRegisteredCallbackCount());
pw.println("IP Configurations:");
pw.increaseIndent();