Loading services/java/com/android/server/connectivity/Vpn.java +202 −2 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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; Loading Loading @@ -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); } } } } } Loading
services/java/com/android/server/connectivity/Vpn.java +202 −2 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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; Loading Loading @@ -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); } } } } }