Add support for USB Ethernet adapters

Change-Id: Ie75cbcdb1b0c8965b59b89675a05175a1e9ebdd6
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
new file mode 100644
index 0000000..df5fdd0
--- /dev/null
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2010 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 android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.DhcpInfoInternal;
+import android.net.LinkAddress;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkStateTracker;
+import android.net.NetworkUtils;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * This class tracks the data connection associated with Ethernet
+ * This is a singleton class and an instance will be created by
+ * ConnectivityService.
+ * @hide
+ */
+public class EthernetDataTracker implements NetworkStateTracker {
+    private static final String NETWORKTYPE = "ETHERNET";
+    private static final String TAG = "Ethernet";
+
+    private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
+    private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
+    private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
+    private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
+
+    private LinkProperties mLinkProperties;
+    private LinkCapabilities mLinkCapabilities;
+    private NetworkInfo mNetworkInfo;
+    private InterfaceObserver mInterfaceObserver;
+
+    /* For sending events to connectivity service handler */
+    private Handler mCsHandler;
+    private Context mContext;
+
+    private static EthernetDataTracker sInstance;
+    private static String mIface = "";
+
+    private static class InterfaceObserver extends INetworkManagementEventObserver.Stub {
+        private EthernetDataTracker mTracker;
+
+        InterfaceObserver(EthernetDataTracker tracker) {
+            super();
+            mTracker = tracker;
+        }
+
+        public void interfaceLinkStatusChanged(String iface, boolean up) {
+            Log.d(TAG, "Interface " + iface + " link " + (up ? "up" : "down"));
+        }
+
+        public void interfaceAdded(String iface) {
+            mTracker.interfaceAdded(iface);
+        }
+
+        public void interfaceRemoved(String iface) {
+            mTracker.interfaceRemoved(iface);
+        }
+    }
+
+    private EthernetDataTracker() {
+        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORKTYPE, "");
+        mLinkProperties = new LinkProperties();
+        mLinkCapabilities = new LinkCapabilities();
+
+        mNetworkInfo.setIsAvailable(false);
+        setTeardownRequested(false);
+    }
+
+    private void interfaceAdded(String iface) {
+        if (!iface.matches("eth\\d"))
+            return;
+
+        Log.d(TAG, "Adding " + iface);
+
+        synchronized(mIface) {
+            if(!mIface.isEmpty())
+                return;
+            mIface = iface;
+        }
+
+        mNetworkInfo.setIsAvailable(true);
+        Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
+        msg.sendToTarget();
+
+        runDhcp();
+    }
+
+    private void interfaceRemoved(String iface) {
+        if (!iface.equals(mIface))
+            return;
+
+        Log.d(TAG, "Removing " + iface);
+
+        NetworkUtils.stopDhcp(mIface);
+
+        mLinkProperties.clear();
+        mNetworkInfo.setIsAvailable(false);
+        mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
+
+        Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
+        msg.sendToTarget();
+
+        msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
+        msg.sendToTarget();
+
+        mIface = "";
+    }
+
+    private void runDhcp() {
+        Thread dhcpThread = new Thread(new Runnable() {
+            public void run() {
+                DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
+                if (!NetworkUtils.runDhcp(mIface, dhcpInfoInternal)) {
+                    Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
+                    return;
+                }
+                mLinkProperties = dhcpInfoInternal.makeLinkProperties();
+                mLinkProperties.setInterfaceName(mIface);
+
+                mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
+                Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
+                msg.sendToTarget();
+            }
+        });
+        dhcpThread.start();
+    }
+
+    public static synchronized EthernetDataTracker getInstance() {
+        if (sInstance == null) sInstance = new EthernetDataTracker();
+        return sInstance;
+    }
+
+    public Object Clone() throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+
+    public void setTeardownRequested(boolean isRequested) {
+        mTeardownRequested.set(isRequested);
+    }
+
+    public boolean isTeardownRequested() {
+        return mTeardownRequested.get();
+    }
+
+    /**
+     * Begin monitoring connectivity
+     */
+    public void startMonitoring(Context context, Handler target) {
+        mContext = context;
+        mCsHandler = target;
+
+        // register for notifications from NetworkManagement Service
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+        mInterfaceObserver = new InterfaceObserver(this);
+        try {
+            service.registerObserver(mInterfaceObserver);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not register InterfaceObserver " + e);
+        }
+    }
+
+    /**
+     * Disable connectivity to a network
+     * TODO: do away with return value after making MobileDataStateTracker async
+     */
+    public boolean teardown() {
+        mTeardownRequested.set(true);
+        NetworkUtils.stopDhcp(mIface);
+        return true;
+    }
+
+    /**
+     * Re-enable connectivity to a network after a {@link #teardown()}.
+     */
+    public boolean reconnect() {
+        mTeardownRequested.set(false);
+        runDhcp();
+        return true;
+    }
+
+    /**
+     * Turn the wireless radio off for a network.
+     * @param turnOn {@code true} to turn the radio on, {@code false}
+     */
+    public boolean setRadio(boolean turnOn) {
+        return true;
+    }
+
+    /**
+     * @return true - If are we currently tethered with another device.
+     */
+    public synchronized boolean isAvailable() {
+        return mNetworkInfo.isAvailable();
+    }
+
+    /**
+     * Tells the underlying networking system that the caller wants to
+     * begin using the named feature. The interpretation of {@code feature}
+     * is completely up to each networking implementation.
+     * @param feature the name of the feature to be used
+     * @param callingPid the process ID of the process that is issuing this request
+     * @param callingUid the user ID of the process that is issuing this request
+     * @return an integer value representing the outcome of the request.
+     * The interpretation of this value is specific to each networking
+     * implementation+feature combination, except that the value {@code -1}
+     * always indicates failure.
+     * TODO: needs to go away
+     */
+    public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
+        return -1;
+    }
+
+    /**
+     * Tells the underlying networking system that the caller is finished
+     * using the named feature. The interpretation of {@code feature}
+     * is completely up to each networking implementation.
+     * @param feature the name of the feature that is no longer needed.
+     * @param callingPid the process ID of the process that is issuing this request
+     * @param callingUid the user ID of the process that is issuing this request
+     * @return an integer value representing the outcome of the request.
+     * The interpretation of this value is specific to each networking
+     * implementation+feature combination, except that the value {@code -1}
+     * always indicates failure.
+     * TODO: needs to go away
+     */
+    public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
+        return -1;
+    }
+
+    /**
+     * @param enabled
+     */
+    public void setDataEnable(boolean enabled) {
+        Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
+    }
+
+    /**
+     * Check if private DNS route is set for the network
+     */
+    public boolean isPrivateDnsRouteSet() {
+        return mPrivateDnsRouteSet.get();
+    }
+
+    /**
+     * Set a flag indicating private DNS route is set
+     */
+    public void privateDnsRouteSet(boolean enabled) {
+        mPrivateDnsRouteSet.set(enabled);
+    }
+
+    /**
+     * Fetch NetworkInfo for the network
+     */
+    public synchronized NetworkInfo getNetworkInfo() {
+        return mNetworkInfo;
+    }
+
+    /**
+     * Fetch LinkProperties for the network
+     */
+    public synchronized LinkProperties getLinkProperties() {
+        return new LinkProperties(mLinkProperties);
+    }
+
+   /**
+     * A capability is an Integer/String pair, the capabilities
+     * are defined in the class LinkSocket#Key.
+     *
+     * @return a copy of this connections capabilities, may be empty but never null.
+     */
+    public LinkCapabilities getLinkCapabilities() {
+        return new LinkCapabilities(mLinkCapabilities);
+    }
+
+    /**
+     * Fetch default gateway address for the network
+     */
+    public int getDefaultGatewayAddr() {
+        return mDefaultGatewayAddr.get();
+    }
+
+    /**
+     * Check if default route is set
+     */
+    public boolean isDefaultRouteSet() {
+        return mDefaultRouteSet.get();
+    }
+
+    /**
+     * Set a flag indicating default route is set for the network
+     */
+    public void defaultRouteSet(boolean enabled) {
+        mDefaultRouteSet.set(enabled);
+    }
+
+    /**
+     * Return the system properties name associated with the tcp buffer sizes
+     * for this network.
+     */
+    public String getTcpBufferSizesPropName() {
+        return "net.tcp.buffersize.wifi";
+    }
+}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index b7d0a8f..1ab22c0 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -24,6 +24,7 @@
 import android.database.ContentObserver;
 import android.net.ConnectivityManager;
 import android.net.DummyDataStateTracker;
+import android.net.EthernetDataTracker;
 import android.net.IConnectivityManager;
 import android.net.LinkProperties;
 import android.net.MobileDataStateTracker;
@@ -420,6 +421,10 @@
                 mNetTrackers[netType] = BluetoothTetheringDataTracker.getInstance();
                 mNetTrackers[netType].startMonitoring(context, mHandler);
                 break;
+            case ConnectivityManager.TYPE_ETHERNET:
+                mNetTrackers[netType] = EthernetDataTracker.getInstance();
+                mNetTrackers[netType].startMonitoring(context, mHandler);
+                break;
             default:
                 loge("Trying to create a DataStateTracker for an unknown radio type " +
                         mNetAttributes[netType].mRadio);