diff options
3 files changed, 77 insertions, 61 deletions
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java index 572a1d7..310b2df 100644 --- a/core/java/com/android/internal/net/VpnConfig.java +++ b/core/java/com/android/internal/net/VpnConfig.java @@ -35,6 +35,8 @@ public class VpnConfig implements Parcelable { public static final String ACTION_VPN_REVOKED = "android.net.vpn.action.REVOKED"; + public static final String LEGACY_VPN = "[Legacy VPN]"; + public static Intent getIntentForConfirmation() { Intent intent = new Intent(); intent.setClassName("com.android.vpndialogs", "com.android.vpndialogs.ConfirmDialog"); diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java index c076ba0..206ce23 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java @@ -72,8 +72,7 @@ public class ManageDialog extends Activity implements Handler.Callback, mDataTransmitted = (TextView) view.findViewById(R.id.data_transmitted); mDataReceived = (TextView) view.findViewById(R.id.data_received); - if (mConfig.packageName == null) { - // Legacy VPN does not have a package name. + if (mConfig.packageName.equals(VpnConfig.LEGACY_VPN)) { mDialog = new AlertDialog.Builder(this) .setIcon(android.R.drawable.ic_dialog_info) .setTitle(R.string.legacy_title) @@ -126,7 +125,7 @@ public class ManageDialog extends Activity implements Handler.Callback, if (which == AlertDialog.BUTTON_POSITIVE) { mConfig.configureIntent.send(); } else if (which == AlertDialog.BUTTON_NEUTRAL) { - mService.prepareVpn(""); + mService.prepareVpn(VpnConfig.LEGACY_VPN); } } catch (Exception e) { Log.e(TAG, "onClick", e); diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java index a8be916..760a5b6 100644 --- a/services/java/com/android/server/connectivity/Vpn.java +++ b/services/java/com/android/server/connectivity/Vpn.java @@ -55,9 +55,8 @@ public class Vpn extends INetworkManagementEventObserver.Stub { private final Context mContext; private final VpnCallback mCallback; - private String mPackageName; + private String mPackageName = VpnConfig.LEGACY_VPN; private String mInterfaceName; - private LegacyVpnRunner mLegacyVpnRunner; public Vpn(Context context, VpnCallback callback) { @@ -66,13 +65,37 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } /** - * Prepare for a VPN application. + * Protect a socket from routing changes by binding it to the given + * interface. The socket IS closed by this method. * - * @param packageName The package name of the new VPN application. - * @return The name of the current prepared package. + * @param socket The socket to be bound. + * @param name The name of the interface. + */ + public void protect(ParcelFileDescriptor socket, String name) { + try { + mContext.enforceCallingPermission(VPN, "protect"); + jniProtectSocket(socket.getFd(), name); + } finally { + try { + socket.close(); + } catch (Exception e) { + // ignore + } + } + } + + /** + * Prepare for a VPN application. If the new application is valid, + * the previous prepared application is revoked. Since legacy VPN + * is not a real application, it uses {@link VpnConfig#LEGACY_VPN} + * as its package name. Note that this method does not check if + * the applications are the same. + * + * @param packageName The package name of the VPN application. + * @return The package name of the current prepared application. */ public synchronized String prepare(String packageName) { - // Return the current prepared package if the new one is null. + // Return the current prepared application if the new one is null. if (packageName == null) { return mPackageName; } @@ -84,9 +107,8 @@ public class Vpn extends INetworkManagementEventObserver.Stub { // Check the permission of the given package. PackageManager pm = mContext.getPackageManager(); - if (packageName.isEmpty()) { - packageName = null; - } else if (pm.checkPermission(VPN, packageName) != PackageManager.PERMISSION_GRANTED) { + if (!packageName.equals(VpnConfig.LEGACY_VPN) && + pm.checkPermission(VPN, packageName) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException(packageName + " does not have " + VPN); } @@ -98,16 +120,13 @@ public class Vpn extends INetworkManagementEventObserver.Stub { mInterfaceName = null; } - // Notify the package being revoked. - if (mPackageName != null) { + // Send out the broadcast or stop LegacyVpnRunner. + if (!mPackageName.equals(VpnConfig.LEGACY_VPN)) { Intent intent = new Intent(VpnConfig.ACTION_VPN_REVOKED); intent.setPackage(mPackageName); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendBroadcast(intent); - } - - // Stop legacy VPN if it has been started. - if (mLegacyVpnRunner != null) { + } else if (mLegacyVpnRunner != null) { mLegacyVpnRunner.exit(); mLegacyVpnRunner = null; } @@ -118,30 +137,12 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } /** - * Protect a socket from routing changes by binding it to the given - * interface. The socket IS closed by this method. - * - * @param socket The socket to be bound. - * @param name The name of the interface. - */ - public void protect(ParcelFileDescriptor socket, String name) { - try { - mContext.enforceCallingPermission(VPN, "protect"); - jniProtectSocket(socket.getFd(), name); - } finally { - try { - socket.close(); - } catch (Exception e) { - // ignore - } - } - } - - /** - * Configure a TUN interface and return its file descriptor. + * Establish a VPN network and return the file descriptor of the VPN + * interface. This methods returns {@code null} if the application is + * not prepared or revoked. * - * @param config The parameters to configure the interface. - * @return The file descriptor of the interface. + * @param config The parameters to configure the network. + * @return The file descriptor of the VPN interface. */ public synchronized ParcelFileDescriptor establish(VpnConfig config) { // Check the permission of the caller. @@ -175,7 +176,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { icon.draw(new Canvas(bitmap)); } - // Create the interface and abort if any of the following steps fails. + // Configure the interface. Abort if any of these steps fails. ParcelFileDescriptor descriptor = ParcelFileDescriptor.adoptFd(jniCreateInterface(config.mtu)); try { @@ -226,8 +227,8 @@ public class Vpn extends INetworkManagementEventObserver.Stub { // INetworkManagementEventObserver.Stub public synchronized void interfaceRemoved(String name) { if (name.equals(mInterfaceName) && jniCheckInterface(name) == 0) { - hideNotification(); mCallback.restore(); + hideNotification(); mInterfaceName = null; } } @@ -277,23 +278,28 @@ public class Vpn extends INetworkManagementEventObserver.Stub { private native void jniProtectSocket(int fd, String name); /** - * Handle legacy VPN requests. This method stops the daemons and restart - * them if their arguments are not null. Heavy things are offloaded to - * another thread, so callers will not be blocked too long. + * Handle a legacy VPN request. This method stops the daemons and restart + * them if arguments are not null. Heavy things are offloaded to another + * thread, so callers will not be blocked for a long time. * + * @param config The parameters to configure the network. * @param raoocn The arguments to be passed to racoon. * @param mtpd The arguments to be passed to mtpd. */ - public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) { - // Stop the current VPN just like a normal VPN application. - prepare(""); + public synchronized void doLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) { + // There is nothing to stop if another VPN application is prepared. + if (config == null && !mPackageName.equals(VpnConfig.LEGACY_VPN)) { + return; + } - // Legacy VPN does not have a package name. - config.packageName = null; + // Reset everything. This also checks the caller. + prepare(VpnConfig.LEGACY_VPN); // Start a new runner and we are done! - mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd); - mLegacyVpnRunner.start(); + if (config != null) { + mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd); + mLegacyVpnRunner.start(); + } } /** @@ -317,9 +323,12 @@ public class Vpn extends INetworkManagementEventObserver.Stub { mConfig = config; mDaemons = new String[] {"racoon", "mtpd"}; mArguments = new String[][] {racoon, mtpd}; + + mConfig.packageName = VpnConfig.LEGACY_VPN; } public void exit() { + // We assume that everything is reset after the daemons die. for (String daemon : mDaemons) { SystemProperties.set("ctl.stop", daemon); } @@ -376,7 +385,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { checkpoint(true); } - // Check if we need to restart some daemons. + // Check if we need to restart any of the daemons. boolean restart = false; for (String[] arguments : mArguments) { restart = restart || (arguments != null); @@ -468,16 +477,24 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } } - // TODO: support routes and search domains for IPSec Mode-CFG. + // TODO: support routes and search domains from IPSec Mode-CFG. - // This is it! Here is the end of our journey! + // Set the routes as requested. + if (mConfig.routes != null) { + jniSetRoutes(mConfig.interfaceName, mConfig.routes); + } + + // The final step must be synchronized. synchronized (Vpn.this) { // Check if the thread is interrupted while we are waiting. checkpoint(false); - if (mConfig.routes != null) { - jniSetRoutes(mConfig.interfaceName, mConfig.routes); + // Check if the interface is gone while we are waiting. + if (jniCheckInterface(mConfig.interfaceName) == 0) { + throw new IllegalStateException(mConfig.interfaceName + " is gone"); } + + // Now INetworkManagementEventObserver is watching our back. mInterfaceName = mConfig.interfaceName; mCallback.override(mConfig.dnsServers, mConfig.searchDomains); showNotification(mConfig, null, null); @@ -485,9 +502,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { Log.i(TAG, "Connected!"); } catch (Exception e) { Log.i(TAG, "Abort due to " + e.getMessage()); - for (String daemon : mDaemons) { - SystemProperties.set("ctl.stop", daemon); - } + exit(); } } } |