The service part of the user space VPN support.

The dialogs will be in another change.

Change-Id: I0cdfd2ef21ffd40ee955b3cbde5ada65dbfdb0bc
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ea750da..3025462 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -19,6 +19,8 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.os.Binder;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 
 import java.net.InetAddress;
@@ -756,4 +758,43 @@
         } catch (RemoteException e) {
         }
     }
+
+    /**
+     * Protect a socket from routing changes. This method is limited to VPN
+     * applications, and it is always hidden to avoid direct use.
+     * @hide
+     */
+    public void protectVpn(ParcelFileDescriptor socket) {
+        try {
+            mService.protectVpn(socket);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Prepare for a VPN application. This method is limited to VpnDialogs,
+     * and it is always hidden to avoid direct use.
+     * @hide
+     */
+    public String prepareVpn(String packageName) {
+        try {
+            return mService.prepareVpn(packageName);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Configure a TUN interface and return its file descriptor. Parameters
+     * are encoded and opaque to this class. This method is limited to VPN
+     * applications, and it is always hidden to avoid direct use.
+     * @hide
+     */
+    public ParcelFileDescriptor establishVpn(Bundle config) {
+        try {
+            return mService.establishVpn(config);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 07f6cec..7f3775d 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -20,7 +20,9 @@
 import android.net.NetworkInfo;
 import android.net.NetworkState;
 import android.net.ProxyProperties;
+import android.os.Bundle;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 
 /**
  * Interface that answers queries about, and allows changing, the
@@ -95,4 +97,10 @@
     ProxyProperties getProxy();
 
     void setDataDependency(int networkType, boolean met);
+
+    void protectVpn(in ParcelFileDescriptor socket);
+
+    String prepareVpn(String packageName);
+
+    ParcelFileDescriptor establishVpn(in Bundle config);
 }
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index e3d4c45..c6f4c20 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -48,6 +48,7 @@
 import android.net.vpn.VpnManager;
 import android.net.wifi.WifiStateTracker;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -55,6 +56,7 @@
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -67,6 +69,8 @@
 
 import com.android.internal.telephony.Phone;
 import com.android.server.connectivity.Tethering;
+import com.android.server.connectivity.Vpn;
+
 import com.google.android.collect.Lists;
 
 import java.io.FileDescriptor;
@@ -103,6 +107,8 @@
     private Tethering mTethering;
     private boolean mTetheringConfigValid = false;
 
+    private Vpn mVpn;
+
     /** Currently active network rules by UID. */
     private SparseIntArray mUidRules = new SparseIntArray();
 
@@ -461,8 +467,11 @@
                                   mTethering.getTetherableBluetoothRegexs().length != 0) &&
                                  mTethering.getUpstreamIfaceRegexs().length != 0);
 
+        mVpn = new Vpn(mContext, new VpnCallback());
+
         try {
             nmService.registerObserver(mTethering);
+            nmService.registerObserver(mVpn);
         } catch (RemoteException e) {
             loge("Error registering observer :" + e);
         }
@@ -2358,6 +2367,7 @@
     private void loge(String s) {
         Slog.e(TAG, s);
     }
+
     int convertFeatureToNetworkType(String feature){
         int networkType = -1;
         if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
@@ -2385,4 +2395,62 @@
         }
         return value;
     }
+
+    // @see ConnectivityManager#protectVpn(ParcelFileDescriptor)
+    // Permission checks are done in Vpn class.
+    @Override
+    public void protectVpn(ParcelFileDescriptor socket) {
+        mVpn.protect(socket, getDefaultInterface());
+    }
+
+    // @see ConnectivityManager#prepareVpn(String)
+    // Permission checks are done in Vpn class.
+    @Override
+    public String prepareVpn(String packageName) {
+        return mVpn.prepare(packageName);
+    }
+
+    // @see ConnectivityManager#establishVpn(Bundle)
+    // Permission checks are done in Vpn class.
+    @Override
+    public ParcelFileDescriptor establishVpn(Bundle config) {
+        return mVpn.establish(config);
+    }
+
+    private String getDefaultInterface() {
+        if (ConnectivityManager.isNetworkTypeValid(mActiveDefaultNetwork)) {
+            NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork];
+            if (tracker != null) {
+                LinkProperties properties = tracker.getLinkProperties();
+                if (properties != null) {
+                    return properties.getInterfaceName();
+                }
+            }
+        }
+        throw new IllegalStateException("No default interface");
+    }
+
+    /**
+     * Callback for VPN subsystem. Currently VPN is not adapted to the service
+     * through NetworkStateTracker since it works differently. For example, it
+     * needs to override DNS servers but never takes the default routes. It
+     * relies on another data network, and it could keep existing connections
+     * alive after reconnecting, switching between networks, or even resuming
+     * from deep sleep. Calls from applications should be done synchronously
+     * to avoid race conditions. As these are all hidden APIs, refactoring can
+     * be done whenever a better abstraction is developed.
+     */
+    public class VpnCallback {
+
+        private VpnCallback() {
+        }
+
+        public synchronized void override(String[] dnsServers) {
+            // TODO: override DNS servers and http proxy.
+        }
+
+        public synchronized void restore() {
+            // TODO: restore VPN changes.
+        }
+    }
 }