VPN: give legacy VPN a fake package name.
And add more comments.
Change-Id: I0cbc495fcb2c8dd105e16a841619b3b9bd227eae
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 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 @@
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 @@
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 @@
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,58 +65,6 @@
}
/**
- * Prepare for a VPN application.
- *
- * @param packageName The package name of the new VPN application.
- * @return The name of the current prepared package.
- */
- public synchronized String prepare(String packageName) {
- // Return the current prepared package if the new one is null.
- if (packageName == null) {
- return mPackageName;
- }
-
- // Only system user can call this method.
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Unauthorized Caller");
- }
-
- // 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) {
- throw new SecurityException(packageName + " does not have " + VPN);
- }
-
- // Reset the interface and hide the notification.
- if (mInterfaceName != null) {
- jniResetInterface(mInterfaceName);
- mCallback.restore();
- hideNotification();
- mInterfaceName = null;
- }
-
- // Notify the package being revoked.
- if (mPackageName != null) {
- 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) {
- mLegacyVpnRunner.exit();
- mLegacyVpnRunner = null;
- }
-
- Log.i(TAG, "Switched from " + mPackageName + " to " + packageName);
- mPackageName = packageName;
- return mPackageName;
- }
-
- /**
* Protect a socket from routing changes by binding it to the given
* interface. The socket IS closed by this method.
*
@@ -138,10 +85,64 @@
}
/**
- * Configure a TUN interface and return its file descriptor.
+ * 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 config The parameters to configure the interface.
- * @return The file descriptor of the interface.
+ * @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 application if the new one is null.
+ if (packageName == null) {
+ return mPackageName;
+ }
+
+ // Only system user can call this method.
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Unauthorized Caller");
+ }
+
+ // Check the permission of the given package.
+ PackageManager pm = mContext.getPackageManager();
+ if (!packageName.equals(VpnConfig.LEGACY_VPN) &&
+ pm.checkPermission(VPN, packageName) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(packageName + " does not have " + VPN);
+ }
+
+ // Reset the interface and hide the notification.
+ if (mInterfaceName != null) {
+ jniResetInterface(mInterfaceName);
+ mCallback.restore();
+ hideNotification();
+ mInterfaceName = 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);
+ } else if (mLegacyVpnRunner != null) {
+ mLegacyVpnRunner.exit();
+ mLegacyVpnRunner = null;
+ }
+
+ Log.i(TAG, "Switched from " + mPackageName + " to " + packageName);
+ mPackageName = packageName;
+ return mPackageName;
+ }
+
+ /**
+ * 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 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
}
}
- // 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 @@
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();
}
}
}