Use IpManager on Ethernet

Bug: 17733693
Bug: 26991160
Change-Id: Idaee8c64bf452e58b93031d8d7ad666edb0c82ae
diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java
index 6074dda..3c230fb 100644
--- a/java/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -31,8 +31,8 @@
 import android.net.NetworkFactory;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkUtils;
 import android.net.StaticIpConfiguration;
+import android.net.ip.IpManager;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
@@ -100,6 +100,8 @@
     private static boolean mLinkUp;
     private NetworkInfo mNetworkInfo;
     private LinkProperties mLinkProperties;
+    private IpManager mIpManager;
+    private Thread mIpProvisioningThread;
 
     EthernetNetworkFactory(RemoteCallbackList<IEthernetServiceListener> listeners) {
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, "");
@@ -120,6 +122,21 @@
         }
     }
 
+    private void stopIpManagerLocked() {
+        if (mIpManager != null) {
+            mIpManager.shutdown();
+            mIpManager = null;
+        }
+    }
+
+    private void stopIpProvisioningThreadLocked() {
+        stopIpManagerLocked();
+
+        if (mIpProvisioningThread != null) {
+            mIpProvisioningThread.interrupt();
+            mIpProvisioningThread = null;
+        }
+    }
 
     /**
      * Updates interface state variables.
@@ -137,6 +154,7 @@
             if (!up) {
                 // Tell the agent we're disconnected. It will call disconnect().
                 mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
+                stopIpProvisioningThreadLocked();
             }
             updateAgent();
             // set our score lower than any network could go
@@ -166,7 +184,6 @@
     private void setInterfaceUp(String iface) {
         // Bring up the interface so we get link status indications.
         try {
-            NetworkUtils.stopDhcp(iface);
             mNMService.setInterfaceUp(iface);
             String hwAddr = null;
             InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
@@ -209,7 +226,7 @@
         Log.d(TAG, "Stopped tracking interface " + iface);
         // TODO: Unify this codepath with stop().
         synchronized (this) {
-            NetworkUtils.stopDhcp(mIface);
+            stopIpProvisioningThreadLocked();
             setInterfaceInfoLocked("", null);
             mNetworkInfo.setExtraInfo(null);
             mLinkUp = false;
@@ -257,12 +274,52 @@
         }
     }
 
+    private class IpManagerCallback extends IpManager.Callback {
+        private LinkProperties mCallbackLinkProperties;
+
+        public LinkProperties waitForProvisioning() {
+            synchronized (this) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {}
+                return mCallbackLinkProperties;
+            }
+        }
+
+        @Override
+        public void onProvisioningSuccess(LinkProperties newLp) {
+            synchronized (this) {
+                mCallbackLinkProperties = newLp;
+                notify();
+            }
+        }
+
+        @Override
+        public void onProvisioningFailure(LinkProperties newLp) {
+            synchronized (this) {
+                mCallbackLinkProperties = null;
+                notify();
+            }
+        }
+    }
+
+
     /* Called by the NetworkFactory on the handler thread. */
     public void onRequestNetwork() {
+        synchronized(EthernetNetworkFactory.this) {
+            if (mIpProvisioningThread != null) {
+                return;
+            }
+        }
+
         // TODO: Handle DHCP renew.
-        Thread dhcpThread = new Thread(new Runnable() {
+        final Thread ipProvisioningThread = new Thread(new Runnable() {
             public void run() {
-                if (DBG) Log.i(TAG, "dhcpThread(" + mIface + "): mNetworkInfo=" + mNetworkInfo);
+                if (DBG) {
+                    Log.d(TAG, String.format("starting ipProvisioningThread(%s): mNetworkInfo=%s",
+                            mIface, mNetworkInfo));
+                }
+
                 LinkProperties linkProperties;
 
                 IpConfiguration config = mEthernetManager.getConfiguration();
@@ -275,39 +332,41 @@
                     linkProperties = config.getStaticIpConfiguration().toLinkProperties(mIface);
                 } else {
                     mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr);
-
-                    DhcpResults dhcpResults = new DhcpResults();
-                    // TODO: Handle DHCP renewals better.
-                    // In general runDhcp handles DHCP renewals for us, because
-                    // the dhcp client stays running, but if the renewal fails,
-                    // we will lose our IP address and connectivity without
-                    // noticing.
-                    if (!NetworkUtils.runDhcp(mIface, dhcpResults)) {
-                        Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
+                    IpManagerCallback blockingCallback = new IpManagerCallback();
+                    synchronized(EthernetNetworkFactory.this) {
+                        stopIpManagerLocked();
+                        mIpManager = new IpManager(mContext, mIface, blockingCallback);
+                        mIpManager.startProvisioning();
+                    }
+                    linkProperties = blockingCallback.waitForProvisioning();
+                    if (linkProperties == null) {
+                        Log.e(TAG, "IP provisioning error");
                         // set our score lower than any network could go
                         // so we get dropped.
                         mFactory.setScoreFilter(-1);
-                        // If DHCP timed out (as opposed to failing), the DHCP client will still be
-                        // running, because in M we changed its timeout to infinite. Stop it now.
-                        NetworkUtils.stopDhcp(mIface);
+                        synchronized(EthernetNetworkFactory.this) {
+                            stopIpManagerLocked();
+                        }
                         return;
                     }
-                    linkProperties = dhcpResults.toLinkProperties(mIface);
                 }
+
                 if (config.getProxySettings() == ProxySettings.STATIC ||
                         config.getProxySettings() == ProxySettings.PAC) {
                     linkProperties.setHttpProxy(config.getHttpProxy());
                 }
 
-                String tcpBufferSizes = mContext.getResources().getString(
+                final String tcpBufferSizes = mContext.getResources().getString(
                         com.android.internal.R.string.config_ethernet_tcp_buffers);
-                if (TextUtils.isEmpty(tcpBufferSizes) == false) {
+                if (!TextUtils.isEmpty(tcpBufferSizes)) {
                     linkProperties.setTcpBufferSizes(tcpBufferSizes);
                 }
 
                 synchronized(EthernetNetworkFactory.this) {
                     if (mNetworkAgent != null) {
                         Log.e(TAG, "Already have a NetworkAgent - aborting new request");
+                        stopIpManagerLocked();
+                        mIpProvisioningThread = null;
                         return;
                     }
                     mLinkProperties = linkProperties;
@@ -321,7 +380,7 @@
                         public void unwanted() {
                             synchronized(EthernetNetworkFactory.this) {
                                 if (this == mNetworkAgent) {
-                                    NetworkUtils.stopDhcp(mIface);
+                                    stopIpManagerLocked();
 
                                     mLinkProperties.clear();
                                     mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null,
@@ -340,10 +399,23 @@
                             }
                         };
                     };
+
+                    mIpProvisioningThread = null;
+                }
+
+                if (DBG) {
+                    Log.d(TAG, String.format("exiting ipProvisioningThread(%s): mNetworkInfo=%s",
+                            mIface, mNetworkInfo));
                 }
             }
         });
-        dhcpThread.start();
+
+        synchronized(EthernetNetworkFactory.this) {
+            if (mIpProvisioningThread == null) {
+                mIpProvisioningThread = ipProvisioningThread;
+                mIpProvisioningThread.start();
+            }
+        }
     }
 
     /**
@@ -402,7 +474,7 @@
     }
 
     public synchronized void stop() {
-        NetworkUtils.stopDhcp(mIface);
+        stopIpProvisioningThreadLocked();
         // ConnectivityService will only forget our NetworkAgent if we send it a NetworkInfo object
         // with a state of DISCONNECTED or SUSPENDED. So we can't simply clear our NetworkInfo here:
         // that sets the state to IDLE, and ConnectivityService will still think we're connected.