summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/connectivity
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/connectivity')
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java38
-rw-r--r--services/java/com/android/server/connectivity/Vpn.java451
2 files changed, 366 insertions, 123 deletions
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 682ecf8..79fb458 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -43,6 +43,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -319,6 +320,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
public void limitReached(String limitName, String iface) {}
+ public void interfaceClassDataActivityChanged(String label, boolean active) {}
+
public int tether(String iface) {
if (DBG) Log.d(TAG, "Tethering " + iface);
TetherInterfaceSM sm = null;
@@ -415,7 +418,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, activeList);
broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER,
erroredList);
- mContext.sendStickyBroadcast(broadcast);
+ mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
if (DBG) {
Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
activeList.size() + ", " + erroredList.size());
@@ -501,8 +504,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mUsbTetherRequested = false;
}
} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
- mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
+ NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
+ ConnectivityManager.EXTRA_NETWORK_INFO);
+ if (networkInfo != null &&
+ networkInfo.getDetailedState() != NetworkInfo.DetailedState.FAILED) {
+ if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
+ }
}
}
}
@@ -1135,7 +1143,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private State mStopTetheringErrorState;
private State mSetDnsForwardersErrorState;
- private ArrayList mNotifyList;
+ private ArrayList<TetherInterfaceSM> mNotifyList;
private int mCurrentConnectionSequence;
private int mMobileApnReserved = ConnectivityManager.TYPE_NONE;
@@ -1165,7 +1173,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mSetDnsForwardersErrorState = new SetDnsForwardersErrorState();
addState(mSetDnsForwardersErrorState);
- mNotifyList = new ArrayList();
+ mNotifyList = new ArrayList<TetherInterfaceSM>();
setInitialState(mInitialState);
}
@@ -1362,8 +1370,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
if (DBG) Log.d(TAG, "notifying tethered with iface =" + ifaceName);
mUpstreamIfaceName = ifaceName;
- for (Object o : mNotifyList) {
- TetherInterfaceSM sm = (TetherInterfaceSM)o;
+ for (TetherInterfaceSM sm : mNotifyList) {
sm.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED,
ifaceName);
}
@@ -1381,13 +1388,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
- if (VDBG) Log.d(TAG, "Tether Mode requested by " + who.toString());
+ if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
mNotifyList.add(who);
transitionTo(mTetherModeAliveState);
break;
case CMD_TETHER_MODE_UNREQUESTED:
who = (TetherInterfaceSM)message.obj;
- if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who.toString());
+ if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
int index = mNotifyList.indexOf(who);
if (index != -1) {
mNotifyList.remove(who);
@@ -1424,18 +1431,29 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
+ if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
mNotifyList.add(who);
who.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED,
mUpstreamIfaceName);
break;
case CMD_TETHER_MODE_UNREQUESTED:
who = (TetherInterfaceSM)message.obj;
+ if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
int index = mNotifyList.indexOf(who);
if (index != -1) {
+ if (DBG) Log.d(TAG, "TetherModeAlive removing notifyee " + who);
mNotifyList.remove(index);
if (mNotifyList.isEmpty()) {
turnOffMasterTetherSettings(); // transitions appropriately
+ } else {
+ if (DBG) {
+ Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() +
+ " live requests:");
+ for (Object o : mNotifyList) Log.d(TAG, " " + o);
+ }
}
+ } else {
+ Log.e(TAG, "TetherModeAliveState UNREQUESTED has unknown who: " + who);
}
break;
case CMD_UPSTREAM_CHANGED:
@@ -1562,7 +1580,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
pw.println();
pw.println("Tether state:");
for (Object o : mIfaces.values()) {
- pw.println(" "+o.toString());
+ pw.println(" " + o);
}
}
pw.println();
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 4b82037..b3cbb84 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -16,8 +16,11 @@
package com.android.server.connectivity;
+import static android.Manifest.permission.BIND_VPN_SERVICE;
+
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -25,55 +28,116 @@ import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.net.BaseNetworkStateTracker;
+import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
+import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
+import android.net.NetworkInfo;
+import android.net.RouteInfo;
+import android.net.NetworkInfo.DetailedState;
import android.os.Binder;
import android.os.FileUtils;
import android.os.IBinder;
+import android.os.INetworkManagementService;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.SystemProperties;
+import android.os.SystemService;
+import android.security.Credentials;
+import android.security.KeyStore;
import android.util.Log;
+import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+import com.android.internal.util.Preconditions;
import com.android.server.ConnectivityService.VpnCallback;
+import com.android.server.net.BaseNetworkObserver;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.Inet4Address;
+import java.net.InetAddress;
import java.nio.charset.Charsets;
import java.util.Arrays;
+import libcore.io.IoUtils;
+
/**
* @hide
*/
-public class Vpn extends INetworkManagementEventObserver.Stub {
-
- private final static String TAG = "Vpn";
-
- private final static String BIND_VPN_SERVICE =
- android.Manifest.permission.BIND_VPN_SERVICE;
+public class Vpn extends BaseNetworkStateTracker {
+ private static final String TAG = "Vpn";
+ private static final boolean LOGD = true;
+
+ // TODO: create separate trackers for each unique VPN to support
+ // automated reconnection
- private final Context mContext;
private final VpnCallback mCallback;
private String mPackage = VpnConfig.LEGACY_VPN;
private String mInterface;
private Connection mConnection;
private LegacyVpnRunner mLegacyVpnRunner;
+ private PendingIntent mStatusIntent;
+ private boolean mEnableNotif = true;
- public Vpn(Context context, VpnCallback callback) {
+ public Vpn(Context context, VpnCallback callback, INetworkManagementService netService) {
+ // TODO: create dedicated TYPE_VPN network type
+ super(ConnectivityManager.TYPE_DUMMY);
mContext = context;
mCallback = callback;
+
+ try {
+ netService.registerObserver(mObserver);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Problem registering observer", e);
+ }
+ }
+
+ public void setEnableNotifications(boolean enableNotif) {
+ mEnableNotif = enableNotif;
+ }
+
+ @Override
+ protected void startMonitoringInternal() {
+ // Ignored; events are sent through callbacks for now
+ }
+
+ @Override
+ public boolean teardown() {
+ // TODO: finish migration to unique tracker for each VPN
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean reconnect() {
+ // TODO: finish migration to unique tracker for each VPN
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getTcpBufferSizesPropName() {
+ return PROP_TCP_BUFFER_UNKNOWN;
+ }
+
+ /**
+ * Update current state, dispaching event to listeners.
+ */
+ private void updateState(DetailedState detailedState, String reason) {
+ if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
+ mNetworkInfo.setDetailedState(detailedState, reason, null);
+ mCallback.onStateChanged(new NetworkInfo(mNetworkInfo));
}
/**
@@ -112,10 +176,13 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
// Reset the interface and hide the notification.
if (mInterface != null) {
jniReset(mInterface);
- long identity = Binder.clearCallingIdentity();
- mCallback.restore();
- hideNotification();
- Binder.restoreCallingIdentity(identity);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mCallback.restore();
+ hideNotification();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
mInterface = null;
}
@@ -136,6 +203,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
mPackage = newPackage;
+ updateState(DetailedState.IDLE, "prepare");
return true;
}
@@ -144,7 +212,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
* interface. The socket is NOT closed by this method.
*
* @param socket The socket to be bound.
- * @param name The name of the interface.
+ * @param interfaze The name of the interface.
*/
public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception {
PackageManager pm = mContext.getPackageManager();
@@ -208,6 +276,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
// Configure the interface. Abort if any of these steps fails.
ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
try {
+ updateState(DetailedState.CONNECTING, "establish");
String interfaze = jniGetName(tun.getFd());
if (jniSetAddresses(interfaze, config.addresses) < 1) {
throw new IllegalArgumentException("At least one address must be specified");
@@ -228,11 +297,8 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
mConnection = connection;
mInterface = interfaze;
} catch (RuntimeException e) {
- try {
- tun.close();
- } catch (Exception ex) {
- // ignore
- }
+ updateState(DetailedState.FAILED, "establish");
+ IoUtils.closeQuietly(tun);
throw e;
}
Log.i(TAG, "Established by " + config.user + " on " + mInterface);
@@ -242,54 +308,61 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
config.interfaze = mInterface;
// Override DNS servers and show the notification.
- long identity = Binder.clearCallingIdentity();
- mCallback.override(config.dnsServers, config.searchDomains);
- showNotification(config, label, bitmap);
- Binder.restoreCallingIdentity(identity);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mCallback.override(config.dnsServers, config.searchDomains);
+ showNotification(config, label, bitmap);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ // TODO: ensure that contract class eventually marks as connected
+ updateState(DetailedState.AUTHENTICATING, "establish");
return tun;
}
- // INetworkManagementEventObserver.Stub
- @Override
- public void interfaceAdded(String interfaze) {
- }
-
- // INetworkManagementEventObserver.Stub
- @Override
- public synchronized void interfaceStatusChanged(String interfaze, boolean up) {
- if (!up && mLegacyVpnRunner != null) {
- mLegacyVpnRunner.check(interfaze);
+ @Deprecated
+ public synchronized void interfaceStatusChanged(String iface, boolean up) {
+ try {
+ mObserver.interfaceStatusChanged(iface, up);
+ } catch (RemoteException e) {
+ // ignored; target is local
}
}
- // INetworkManagementEventObserver.Stub
- @Override
- public void interfaceLinkStateChanged(String interfaze, boolean up) {
- }
-
- // INetworkManagementEventObserver.Stub
- @Override
- public synchronized void interfaceRemoved(String interfaze) {
- if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
- long identity = Binder.clearCallingIdentity();
- mCallback.restore();
- hideNotification();
- Binder.restoreCallingIdentity(identity);
- mInterface = null;
- if (mConnection != null) {
- mContext.unbindService(mConnection);
- mConnection = null;
- } else if (mLegacyVpnRunner != null) {
- mLegacyVpnRunner.exit();
- mLegacyVpnRunner = null;
+ private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
+ @Override
+ public void interfaceStatusChanged(String interfaze, boolean up) {
+ synchronized (Vpn.this) {
+ if (!up && mLegacyVpnRunner != null) {
+ mLegacyVpnRunner.check(interfaze);
+ }
}
}
- }
- // INetworkManagementEventObserver.Stub
- @Override
- public void limitReached(String limit, String interfaze) {
- }
+ @Override
+ public void interfaceRemoved(String interfaze) {
+ synchronized (Vpn.this) {
+ if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mCallback.restore();
+ hideNotification();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ mInterface = null;
+ if (mConnection != null) {
+ mContext.unbindService(mConnection);
+ mConnection = null;
+ updateState(DetailedState.DISCONNECTED, "interfaceRemoved");
+ } else if (mLegacyVpnRunner != null) {
+ mLegacyVpnRunner.exit();
+ mLegacyVpnRunner = null;
+ }
+ }
+ }
+ }
+ };
private void enforceControlPermission() {
// System user is allowed to control VPN.
@@ -326,6 +399,9 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
}
private void showNotification(VpnConfig config, String label, Bitmap icon) {
+ if (!mEnableNotif) return;
+ mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext, config);
+
NotificationManager nm = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -341,15 +417,18 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
.setLargeIcon(icon)
.setContentTitle(title)
.setContentText(text)
- .setContentIntent(VpnConfig.getIntentForStatusPanel(mContext, config))
+ .setContentIntent(mStatusIntent)
.setDefaults(0)
.setOngoing(true)
- .getNotification();
+ .build();
nm.notify(R.drawable.vpn_connected, notification);
}
}
private void hideNotification() {
+ if (!mEnableNotif) return;
+ mStatusIntent = null;
+
NotificationManager nm = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -366,31 +445,172 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
private native int jniCheck(String interfaze);
private native void jniProtect(int socket, String interfaze);
+ private static String findLegacyVpnGateway(LinkProperties prop) {
+ for (RouteInfo route : prop.getRoutes()) {
+ // Currently legacy VPN only works on IPv4.
+ if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
+ return route.getGateway().getHostAddress();
+ }
+ }
+
+ throw new IllegalStateException("Unable to find suitable gateway");
+ }
+
/**
- * Start legacy VPN. 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.
+ * Start legacy VPN, controlling native daemons as needed. Creates a
+ * secondary thread to perform connection work, returning quickly.
*/
- public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
+ public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
+ if (keyStore.state() != KeyStore.State.UNLOCKED) {
+ throw new IllegalStateException("KeyStore isn't unlocked");
+ }
+
+ final String iface = egress.getInterfaceName();
+ final String gateway = findLegacyVpnGateway(egress);
+
+ // Load certificates.
+ String privateKey = "";
+ String userCert = "";
+ String caCert = "";
+ String serverCert = "";
+ if (!profile.ipsecUserCert.isEmpty()) {
+ privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
+ byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
+ userCert = (value == null) ? null : new String(value, Charsets.UTF_8);
+ }
+ if (!profile.ipsecCaCert.isEmpty()) {
+ byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
+ caCert = (value == null) ? null : new String(value, Charsets.UTF_8);
+ }
+ if (!profile.ipsecServerCert.isEmpty()) {
+ byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
+ serverCert = (value == null) ? null : new String(value, Charsets.UTF_8);
+ }
+ if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
+ throw new IllegalStateException("Cannot load credentials");
+ }
+
+ // Prepare arguments for racoon.
+ String[] racoon = null;
+ switch (profile.type) {
+ case VpnProfile.TYPE_L2TP_IPSEC_PSK:
+ racoon = new String[] {
+ iface, profile.server, "udppsk", profile.ipsecIdentifier,
+ profile.ipsecSecret, "1701",
+ };
+ break;
+ case VpnProfile.TYPE_L2TP_IPSEC_RSA:
+ racoon = new String[] {
+ iface, profile.server, "udprsa", privateKey, userCert,
+ caCert, serverCert, "1701",
+ };
+ break;
+ case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
+ racoon = new String[] {
+ iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
+ profile.ipsecSecret, profile.username, profile.password, "", gateway,
+ };
+ break;
+ case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
+ racoon = new String[] {
+ iface, profile.server, "xauthrsa", privateKey, userCert,
+ caCert, serverCert, profile.username, profile.password, "", gateway,
+ };
+ break;
+ case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
+ racoon = new String[] {
+ iface, profile.server, "hybridrsa",
+ caCert, serverCert, profile.username, profile.password, "", gateway,
+ };
+ break;
+ }
+
+ // Prepare arguments for mtpd.
+ String[] mtpd = null;
+ switch (profile.type) {
+ case VpnProfile.TYPE_PPTP:
+ mtpd = new String[] {
+ iface, "pptp", profile.server, "1723",
+ "name", profile.username, "password", profile.password,
+ "linkname", "vpn", "refuse-eap", "nodefaultroute",
+ "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+ (profile.mppe ? "+mppe" : "nomppe"),
+ };
+ break;
+ case VpnProfile.TYPE_L2TP_IPSEC_PSK:
+ case VpnProfile.TYPE_L2TP_IPSEC_RSA:
+ mtpd = new String[] {
+ iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
+ "name", profile.username, "password", profile.password,
+ "linkname", "vpn", "refuse-eap", "nodefaultroute",
+ "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+ };
+ break;
+ }
+
+ VpnConfig config = new VpnConfig();
+ config.legacy = true;
+ config.user = profile.key;
+ config.interfaze = iface;
+ config.session = profile.name;
+ config.routes = profile.routes;
+ if (!profile.dnsServers.isEmpty()) {
+ config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
+ }
+ if (!profile.searchDomains.isEmpty()) {
+ config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
+ }
+
+ startLegacyVpn(config, racoon, mtpd);
+ }
+
+ private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
+ stopLegacyVpn();
+
// Prepare for the new request. This also checks the caller.
prepare(null, VpnConfig.LEGACY_VPN);
+ updateState(DetailedState.CONNECTING, "startLegacyVpn");
// Start a new LegacyVpnRunner and we are done!
mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
mLegacyVpnRunner.start();
}
+ public synchronized void stopLegacyVpn() {
+ if (mLegacyVpnRunner != null) {
+ mLegacyVpnRunner.exit();
+ mLegacyVpnRunner = null;
+
+ synchronized (LegacyVpnRunner.TAG) {
+ // wait for old thread to completely finish before spinning up
+ // new instance, otherwise state updates can be out of order.
+ }
+ }
+ }
+
/**
* Return the information of the current ongoing legacy VPN.
*/
public synchronized LegacyVpnInfo getLegacyVpnInfo() {
// Check if the caller is authorized.
enforceControlPermission();
- return (mLegacyVpnRunner == null) ? null : mLegacyVpnRunner.getInfo();
+ if (mLegacyVpnRunner == null) return null;
+
+ final LegacyVpnInfo info = new LegacyVpnInfo();
+ info.key = mLegacyVpnRunner.mConfig.user;
+ info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
+ if (mNetworkInfo.isConnected()) {
+ info.intent = mStatusIntent;
+ }
+ return info;
+ }
+
+ public VpnConfig getLegacyVpnConfig() {
+ if (mLegacyVpnRunner != null) {
+ return mLegacyVpnRunner.mConfig;
+ } else {
+ return null;
+ }
}
/**
@@ -407,8 +627,6 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
private final String[] mDaemons;
private final String[][] mArguments;
private final LocalSocket[] mSockets;
- private final String mOuterInterface;
- private final LegacyVpnInfo mInfo;
private long mTimer = -1;
@@ -416,20 +634,13 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
super(TAG);
mConfig = config;
mDaemons = new String[] {"racoon", "mtpd"};
+ // TODO: clear arguments from memory once launched
mArguments = new String[][] {racoon, mtpd};
mSockets = new LocalSocket[mDaemons.length];
- mInfo = new LegacyVpnInfo();
-
- // This is the interface which VPN is running on.
- mOuterInterface = mConfig.interfaze;
-
- // Legacy VPN is not a real package, so we use it to carry the key.
- mInfo.key = mConfig.user;
- mConfig.user = VpnConfig.LEGACY_VPN;
}
public void check(String interfaze) {
- if (interfaze.equals(mOuterInterface)) {
+ if (interfaze.equals(mConfig.interfaze)) {
Log.i(TAG, "Legacy VPN is going down with " + interfaze);
exit();
}
@@ -439,21 +650,9 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
// We assume that everything is reset after stopping the daemons.
interrupt();
for (LocalSocket socket : mSockets) {
- try {
- socket.close();
- } catch (Exception e) {
- // ignore
- }
- }
- }
-
- public LegacyVpnInfo getInfo() {
- // Update the info when VPN is disconnected.
- if (mInfo.state == LegacyVpnInfo.STATE_CONNECTED && mInterface == null) {
- mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
- mInfo.intent = null;
+ IoUtils.closeQuietly(socket);
}
- return mInfo;
+ updateState(DetailedState.DISCONNECTED, "exit");
}
@Override
@@ -463,6 +662,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
synchronized (TAG) {
Log.v(TAG, "Executing");
execute();
+ monitorDaemons();
}
}
@@ -474,22 +674,21 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
} else if (now - mTimer <= 60000) {
Thread.sleep(yield ? 200 : 1);
} else {
- mInfo.state = LegacyVpnInfo.STATE_TIMEOUT;
+ updateState(DetailedState.FAILED, "checkpoint");
throw new IllegalStateException("Time is up");
}
}
private void execute() {
// Catch all exceptions so we can clean up few things.
+ boolean initFinished = false;
try {
// Initialize the timer.
checkpoint(false);
- mInfo.state = LegacyVpnInfo.STATE_INITIALIZING;
// Wait for the daemons to stop.
for (String daemon : mDaemons) {
- String key = "init.svc." + daemon;
- while (!"stopped".equals(SystemProperties.get(key, "stopped"))) {
+ while (!SystemService.isStopped(daemon)) {
checkpoint(true);
}
}
@@ -501,6 +700,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
throw new IllegalStateException("Cannot delete the state");
}
new File("/data/misc/vpn/abort").delete();
+ initFinished = true;
// Check if we need to restart any of the daemons.
boolean restart = false;
@@ -508,10 +708,10 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
restart = restart || (arguments != null);
}
if (!restart) {
- mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
+ updateState(DetailedState.DISCONNECTED, "execute");
return;
}
- mInfo.state = LegacyVpnInfo.STATE_CONNECTING;
+ updateState(DetailedState.CONNECTING, "execute");
// Start the daemon with arguments.
for (int i = 0; i < mDaemons.length; ++i) {
@@ -522,11 +722,10 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
// Start the daemon.
String daemon = mDaemons[i];
- SystemProperties.set("ctl.start", daemon);
+ SystemService.start(daemon);
// Wait for the daemon to start.
- String key = "init.svc." + daemon;
- while (!"running".equals(SystemProperties.get(key))) {
+ while (!SystemService.isRunning(daemon)) {
checkpoint(true);
}
@@ -582,8 +781,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
// Check if a running daemon is dead.
for (int i = 0; i < mDaemons.length; ++i) {
String daemon = mDaemons[i];
- if (mArguments[i] != null && !"running".equals(
- SystemProperties.get("init.svc." + daemon))) {
+ if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
throw new IllegalStateException(daemon + " is dead");
}
}
@@ -640,26 +838,53 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
showNotification(mConfig, null, null);
Log.i(TAG, "Connected!");
- mInfo.state = LegacyVpnInfo.STATE_CONNECTED;
- mInfo.intent = VpnConfig.getIntentForStatusPanel(mContext, null);
+ updateState(DetailedState.CONNECTED, "execute");
}
} catch (Exception e) {
Log.i(TAG, "Aborting", e);
exit();
} finally {
// Kill the daemons if they fail to stop.
- if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING) {
+ if (!initFinished) {
for (String daemon : mDaemons) {
- SystemProperties.set("ctl.stop", daemon);
+ SystemService.stop(daemon);
}
}
// Do not leave an unstable state.
- if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING ||
- mInfo.state == LegacyVpnInfo.STATE_CONNECTING) {
- mInfo.state = LegacyVpnInfo.STATE_FAILED;
+ if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
+ updateState(DetailedState.FAILED, "execute");
}
}
}
+
+ /**
+ * Monitor the daemons we started, moving to disconnected state if the
+ * underlying services fail.
+ */
+ private void monitorDaemons() {
+ if (!mNetworkInfo.isConnected()) {
+ return;
+ }
+
+ try {
+ while (true) {
+ Thread.sleep(2000);
+ for (int i = 0; i < mDaemons.length; i++) {
+ if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
+ return;
+ }
+ }
+ }
+ } catch (InterruptedException e) {
+ Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
+ } finally {
+ for (String daemon : mDaemons) {
+ SystemService.stop(daemon);
+ }
+
+ updateState(DetailedState.DISCONNECTED, "babysit");
+ }
+ }
}
}