summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorChia-chi Yeh <chiachi@android.com>2011-06-29 16:05:58 -0700
committerChia-chi Yeh <chiachi@android.com>2011-06-29 16:05:58 -0700
commit85a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758 (patch)
tree9a1fb0d1e21b3beb165104c625c76f2b59d792c7 /services
parentd2ab6d07065479b12730b01cfbe77816e0f8b030 (diff)
downloadframeworks_base-85a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758.zip
frameworks_base-85a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758.tar.gz
frameworks_base-85a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758.tar.bz2
VPN: add a method to handle requests of legacy VPNs.
The code is working but not complete yet. Change-Id: Id8c8f137665373ad52c626b9d34e5a2cad028597
Diffstat (limited to 'services')
-rw-r--r--services/java/com/android/server/connectivity/Vpn.java204
1 files changed, 202 insertions, 2 deletions
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index db3b61e..f5efda9 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -27,15 +27,23 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.net.INetworkManagementEventObserver;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.SystemProperties;
import android.util.Log;
import com.android.internal.R;
import com.android.internal.net.VpnConfig;
import com.android.server.ConnectivityService.VpnCallback;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charsets;
+
/**
* @hide
*/
@@ -49,7 +57,8 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
private String mPackageName;
private String mInterfaceName;
- private String mDnsPropertyPrefix;
+
+ private LegacyVpnRunner mLegacyVpnRunner;
public Vpn(Context context, VpnCallback callback) {
mContext = context;
@@ -249,4 +258,195 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
private native void nativeReset(String name);
private native int nativeCheck(String name);
private native void nativeProtect(int fd, String name);
+
+ /**
+ * Handle legacy VPN requests. This method stops the services and restart
+ * them if their arguments are not null. Heavy things are offloaded to
+ * another thread, so callers will not be blocked too long.
+ *
+ * @param raoocn The arguments to be passed to racoon.
+ * @param mtpd The arguments to be passed to mtpd.
+ */
+ public synchronized void doLegacyVpn(String[] racoon, String[] mtpd) {
+ // Currently only system user is allowed.
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Unauthorized Caller");
+ }
+
+ // If the previous runner is still alive, interrupt it.
+ if (mLegacyVpnRunner != null && mLegacyVpnRunner.isAlive()) {
+ mLegacyVpnRunner.interrupt();
+ }
+
+ // Start a new runner and we are done!
+ mLegacyVpnRunner = new LegacyVpnRunner(
+ new String[] {"racoon", "mtpd"}, racoon, mtpd);
+ mLegacyVpnRunner.start();
+ }
+
+ /**
+ * Bringing up a VPN connection takes time, and that is all this thread
+ * does. Here we have plenty of time. The only thing we need to take
+ * care of is responding to interruptions as soon as possible. Otherwise
+ * requests will be piled up. This can be done in a Handler as a state
+ * machine, but it is much easier to read in the current form.
+ */
+ private class LegacyVpnRunner extends Thread {
+ private static final String TAG = "LegacyVpnRunner";
+
+ private static final String NONE = "--";
+
+ private final String[] mServices;
+ private final String[][] mArguments;
+ private long mTimer = -1;
+
+ public LegacyVpnRunner(String[] services, String[]... arguments) {
+ super(TAG);
+ mServices = services;
+ mArguments = arguments;
+ }
+
+ @Override
+ public void run() {
+ // Wait for the previous thread since it has been interrupted.
+ Log.v(TAG, "wait");
+ synchronized (TAG) {
+ Log.v(TAG, "run");
+ execute();
+ Log.v(TAG, "exit");
+ }
+ }
+
+ private void checkpoint(boolean yield) throws InterruptedException {
+ long now = SystemClock.elapsedRealtime();
+ if (mTimer == -1) {
+ mTimer = now;
+ Thread.sleep(1);
+ } else if (now - mTimer <= 30000) {
+ Thread.sleep(yield ? 200 : 1);
+ } else {
+ throw new InterruptedException("timeout");
+ }
+ }
+
+ private void execute() {
+ // Catch all exceptions so we can clean up few things.
+ try {
+ // Initialize the timer.
+ checkpoint(false);
+
+ // First stop the services.
+ for (String service : mServices) {
+ SystemProperties.set("ctl.stop", service);
+ }
+
+ // Wait for the services to stop.
+ for (String service : mServices) {
+ String key = "init.svc." + service;
+ while (!"stopped".equals(SystemProperties.get(key))) {
+ checkpoint(true);
+ }
+ }
+
+ // Reset the properties.
+ SystemProperties.set("vpn.dns", NONE);
+ SystemProperties.set("vpn.via", NONE);
+ while (!NONE.equals(SystemProperties.get("vpn.dns")) ||
+ !NONE.equals(SystemProperties.get("vpn.via"))) {
+ checkpoint(true);
+ }
+
+ // Check if we need to restart some services.
+ boolean restart = false;
+ for (String[] arguments : mArguments) {
+ restart = restart || (arguments != null);
+ }
+ if (!restart) {
+ return;
+ }
+
+ // Start the service with arguments.
+ for (int i = 0; i < mServices.length; ++i) {
+ String[] arguments = mArguments[i];
+ if (arguments == null) {
+ continue;
+ }
+
+ // Start the service.
+ String service = mServices[i];
+ SystemProperties.set("ctl.start", service);
+
+ // Wait for the service to start.
+ String key = "init.svc." + service;
+ while (!"running".equals(SystemProperties.get(key))) {
+ checkpoint(true);
+ }
+
+ // Create the control socket.
+ LocalSocket socket = new LocalSocket();
+ LocalSocketAddress address = new LocalSocketAddress(
+ service, LocalSocketAddress.Namespace.RESERVED);
+
+ // Wait for the socket to connect.
+ while (true) {
+ try {
+ socket.connect(address);
+ break;
+ } catch (Exception e) {
+ // ignore
+ }
+ checkpoint(true);
+ }
+ socket.setSoTimeout(500);
+
+ // Send over the arguments.
+ OutputStream output = socket.getOutputStream();
+ for (String argument : arguments) {
+ byte[] bytes = argument.getBytes(Charsets.UTF_8);
+ if (bytes.length >= 0xFFFF) {
+ throw new IllegalArgumentException("argument too large");
+ }
+ output.write(bytes.length >> 8);
+ output.write(bytes.length);
+ output.write(bytes);
+ checkpoint(false);
+ }
+
+ // Send End-Of-Arguments.
+ output.write(0xFF);
+ output.write(0xFF);
+ output.flush();
+ socket.close();
+ }
+
+ // Now here is the beast from the old days. We check few
+ // properties to figure out the current status. Ideally we
+ // can read things back from the sockets and get rid of the
+ // properties, but we have no time...
+ while (NONE.equals(SystemProperties.get("vpn.dns")) ||
+ NONE.equals(SystemProperties.get("vpn.via"))) {
+
+ // Check if a running service is dead.
+ for (int i = 0; i < mServices.length; ++i) {
+ String service = mServices[i];
+ if (mArguments[i] != null && !"running".equals(
+ SystemProperties.get("init.svc." + service))) {
+ throw new IllegalArgumentException(service + " is dead");
+ }
+ }
+ checkpoint(true);
+ }
+
+ // Great! Now we are connected!
+ Log.i(TAG, "connected!");
+ // TODO:
+
+ } catch (Exception e) {
+ Log.i(TAG, e.getMessage());
+ for (String service : mServices) {
+ SystemProperties.set("ctl.stop", service);
+ }
+ }
+ }
+ }
}