diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index ea750da004..30254625a2 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -19,6 +19,8 @@ package android.net; 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 @@ public class ConnectivityManager { } 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 07f6cecea5..7f3775dd71 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -20,7 +20,9 @@ import android.net.LinkProperties; 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 @@ interface IConnectivityManager 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 e3d4c45cf6..c6f4c205b0 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -48,6 +48,7 @@ import android.net.RouteInfo; 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.IBinder; 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 android.util.SparseIntArray; 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 @@ public class ConnectivityService extends IConnectivityManager.Stub { 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 @@ public class ConnectivityService extends IConnectivityManager.Stub { 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 @@ public class ConnectivityService extends IConnectivityManager.Stub { 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 @@ public class ConnectivityService extends IConnectivityManager.Stub { } 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. + } + } }