diff options
author | Android (Google) Code Review <android-gerrit@google.com> | 2009-07-30 16:05:45 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2009-07-30 16:05:45 -0700 |
commit | 455266e1737ab0c4935543bd086c926da8ff5771 (patch) | |
tree | 3674bb5db0b971d5a7043409ab1ab9e8d4ce6338 /packages | |
parent | 9225dfa70c19e695d75599f8f8dcb9605ea6e039 (diff) | |
parent | b203a57d1f00fe509e0d065cd928099bb7d19d87 (diff) | |
download | frameworks_base-455266e1737ab0c4935543bd086c926da8ff5771.zip frameworks_base-455266e1737ab0c4935543bd086c926da8ff5771.tar.gz frameworks_base-455266e1737ab0c4935543bd086c926da8ff5771.tar.bz2 |
am b203a57d: Merge change 9114 into donut
Merge commit 'b203a57d1f00fe509e0d065cd928099bb7d19d87'
* commit 'b203a57d1f00fe509e0d065cd928099bb7d19d87':
Add state saving mechanism to support proc restart
Diffstat (limited to 'packages')
8 files changed, 253 insertions, 102 deletions
diff --git a/packages/VpnServices/src/com/android/server/vpn/DaemonProxy.java b/packages/VpnServices/src/com/android/server/vpn/DaemonProxy.java index b749821..289ee45 100644 --- a/packages/VpnServices/src/com/android/server/vpn/DaemonProxy.java +++ b/packages/VpnServices/src/com/android/server/vpn/DaemonProxy.java @@ -25,6 +25,7 @@ import android.util.Log; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.Serializable; /** * Proxy to start, stop and interact with a VPN daemon. @@ -33,7 +34,10 @@ import java.io.OutputStream; * connection with the daemon, to both send commands to the daemon and receive * response and connecting error code from the daemon. */ -class DaemonProxy { +class DaemonProxy implements Serializable { + private static final long serialVersionUID = 1L; + private static final boolean DBG = true; + private static final int WAITING_TIME = 15; // sec private static final String SVC_STATE_CMD_PREFIX = "init.svc."; @@ -45,8 +49,8 @@ class DaemonProxy { private static final int END_OF_ARGUMENTS = 255; private String mName; - private LocalSocket mControlSocket; private String mTag; + private transient LocalSocket mControlSocket; /** * Creates a proxy of the specified daemon. @@ -63,14 +67,8 @@ class DaemonProxy { void start() throws IOException { String svc = mName; - Log.d(mTag, "----- Stop the daemon just in case: " + mName); - SystemProperties.set(SVC_STOP_CMD, mName); - if (!blockUntil(SVC_STATE_STOPPED, 5)) { - throw new IOException("cannot start service anew: " + svc - + ", it is still running"); - } - Log.d(mTag, "+++++ Start: " + svc); + Log.i(mTag, "Start VPN daemon: " + svc); SystemProperties.set(SVC_START_CMD, svc); if (!blockUntil(SVC_STATE_RUNNING, WAITING_TIME)) { @@ -103,7 +101,7 @@ class DaemonProxy { try { mControlSocket.close(); } catch (IOException e) { - Log.e(mTag, "close control socket", e); + Log.w(mTag, "close control socket", e); } finally { mControlSocket = null; } @@ -111,10 +109,10 @@ class DaemonProxy { void stop() { String svc = mName; - Log.d(mTag, "----- Stop: " + svc); + Log.i(mTag, "Stop VPN daemon: " + svc); SystemProperties.set(SVC_STOP_CMD, svc); boolean success = blockUntil(SVC_STATE_STOPPED, 5); - Log.d(mTag, "stopping " + svc + ", success? " + success); + if (DBG) Log.d(mTag, "stopping " + svc + ", success? " + success); } boolean isStopped() { @@ -129,7 +127,7 @@ class DaemonProxy { if (!blocking && in.available() == 0) return 0; int data = in.read(); - Log.d(mTag, "got data from control socket: " + data); + Log.i(mTag, "got data from control socket: " + data); return data; } @@ -146,7 +144,7 @@ class DaemonProxy { s.connect(a); return s; } catch (IOException e) { - Log.d(mTag, "service not yet listen()ing; try again"); + if (DBG) Log.d(mTag, "service not yet listen()ing; try again"); excp = e; sleep(500); } @@ -173,8 +171,10 @@ class DaemonProxy { int n = waitTime * 1000 / sleepTime; for (int i = 0; i < n; i++) { if (expectedState.equals(SystemProperties.get(cmd))) { - Log.d(mTag, mName + " is " + expectedState + " after " - + (i * sleepTime) + " msec"); + if (DBG) { + Log.d(mTag, mName + " is " + expectedState + " after " + + (i * sleepTime) + " msec"); + } break; } sleep(sleepTime); diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java index 8efd7c4..7910f4a 100644 --- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java +++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java @@ -45,4 +45,10 @@ class L2tpIpsecPskService extends VpnService<L2tpIpsecPskProfile> { (p.isSecretEnabled() ? p.getSecretString() : null), username, password); } + + @Override + protected void stopPreviouslyRunDaemons() { + stopDaemon(IPSEC); + stopDaemon(MtpdHelper.MTPD); + } } diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java index 56694b6..13b4952 100644 --- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java +++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java @@ -46,6 +46,12 @@ class L2tpIpsecService extends VpnService<L2tpIpsecProfile> { username, password); } + @Override + protected void stopPreviouslyRunDaemons() { + stopDaemon(IPSEC); + stopDaemon(MtpdHelper.MTPD); + } + private String getCaCertPath() { return CertTool.getInstance().getCaCertificate( getProfile().getCaCertificate()); diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpService.java index 9273f35..d658a36 100644 --- a/packages/VpnServices/src/com/android/server/vpn/L2tpService.java +++ b/packages/VpnServices/src/com/android/server/vpn/L2tpService.java @@ -35,4 +35,9 @@ class L2tpService extends VpnService<L2tpProfile> { (p.isSecretEnabled() ? p.getSecretString() : null), username, password); } + + @Override + protected void stopPreviouslyRunDaemons() { + stopDaemon(MtpdHelper.MTPD); + } } diff --git a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java index 805a5b5..9078d9b 100644 --- a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java +++ b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java @@ -24,26 +24,33 @@ import java.util.Arrays; * A helper class for sending commands to the MTP daemon (mtpd). */ class MtpdHelper { - private static final String MTPD = "mtpd"; + static final String MTPD = "mtpd"; private static final String VPN_LINKNAME = "vpn"; private static final String PPP_ARGS_SEPARATOR = ""; static void sendCommand(VpnService<?> vpnService, String protocol, String serverIp, String port, String secret, String username, String password) throws IOException { + sendCommand(vpnService, protocol, serverIp, port, secret, username, + password, false); + } + + static void sendCommand(VpnService<?> vpnService, String protocol, + String serverIp, String port, String secret, String username, + String password, boolean encryption) throws IOException { ArrayList<String> args = new ArrayList<String>(); args.addAll(Arrays.asList(protocol, serverIp, port)); if (secret != null) args.add(secret); args.add(PPP_ARGS_SEPARATOR); - addPppArguments(vpnService, args, serverIp, username, password); + addPppArguments(args, serverIp, username, password, encryption); DaemonProxy mtpd = vpnService.startDaemon(MTPD); mtpd.sendCommand(args.toArray(new String[args.size()])); } - private static void addPppArguments(VpnService<?> vpnService, - ArrayList<String> args, String serverIp, String username, - String password) throws IOException { + private static void addPppArguments(ArrayList<String> args, String serverIp, + String username, String password, boolean encryption) + throws IOException { args.addAll(Arrays.asList( "linkname", VPN_LINKNAME, "name", username, @@ -52,6 +59,9 @@ class MtpdHelper { "idle", "1800", "mtu", "1400", "mru", "1400")); + if (encryption) { + args.add("+mppe"); + } } private MtpdHelper() { diff --git a/packages/VpnServices/src/com/android/server/vpn/PptpService.java b/packages/VpnServices/src/com/android/server/vpn/PptpService.java index 01362a5..d903d1b 100644 --- a/packages/VpnServices/src/com/android/server/vpn/PptpService.java +++ b/packages/VpnServices/src/com/android/server/vpn/PptpService.java @@ -26,11 +26,17 @@ import java.io.IOException; class PptpService extends VpnService<PptpProfile> { static final String PPTP_DAEMON = "pptp"; static final String PPTP_PORT = "1723"; + @Override protected void connect(String serverIp, String username, String password) throws IOException { + PptpProfile p = getProfile(); MtpdHelper.sendCommand(this, PPTP_DAEMON, serverIp, PPTP_PORT, null, - username, password); + username, password, p.isEncryptionEnabled()); } + @Override + protected void stopPreviouslyRunDaemons() { + stopDaemon(MtpdHelper.MTPD); + } } diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java index 60a07d5..b107c7d 100644 --- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java +++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java @@ -28,7 +28,11 @@ import android.text.TextUtils; import android.util.Log; import java.io.IOException; +import java.io.Serializable; +import java.net.DatagramSocket; +import java.net.Socket; import java.net.InetAddress; +import java.net.NetworkInterface; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; @@ -36,7 +40,9 @@ import java.util.List; /** * The service base class for managing a type of VPN connection. */ -abstract class VpnService<E extends VpnProfile> { +abstract class VpnService<E extends VpnProfile> implements Serializable { + protected static final long serialVersionUID = 1L; + private static final boolean DBG = true; private static final int NOTIFICATION_ID = 1; private static final String DNS1 = "net.dns1"; @@ -50,12 +56,16 @@ abstract class VpnService<E extends VpnProfile> { private static final String REMOTE_IP = "net.ipremote"; private static final String DNS_DOMAIN_SUFFICES = "net.dns.search"; + private static final int CHALLENGE_ERROR_CODE = 5; + private static final int REMOTE_HUNG_UP_ERROR_CODE = 7; private static final int AUTH_ERROR_CODE = 51; private final String TAG = VpnService.class.getSimpleName(); + // FIXME: profile is only needed in connecting phase, so we can just save + // the profile name and service class name for recovery E mProfile; - VpnServiceBinder mContext; + transient VpnServiceBinder mContext; private VpnState mState = VpnState.IDLE; private Throwable mError; @@ -63,9 +73,9 @@ abstract class VpnService<E extends VpnProfile> { // connection settings private String mOriginalDns1; private String mOriginalDns2; - private String mVpnDns1 = ""; - private String mVpnDns2 = ""; private String mOriginalDomainSuffices; + private String mLocalIp; + private String mLocalIf; private long mStartTime; // VPN connection start time @@ -73,7 +83,7 @@ abstract class VpnService<E extends VpnProfile> { private DaemonHelper mDaemonHelper = new DaemonHelper(); // for helping showing, updating notification - private NotificationHelper mNotification = new NotificationHelper(); + private transient NotificationHelper mNotification; /** * Establishes a VPN connection with the specified username and password. @@ -81,6 +91,8 @@ abstract class VpnService<E extends VpnProfile> { protected abstract void connect(String serverIp, String username, String password) throws IOException; + protected abstract void stopPreviouslyRunDaemons(); + /** * Starts a VPN daemon. */ @@ -90,6 +102,13 @@ abstract class VpnService<E extends VpnProfile> { } /** + * Stops a VPN daemon. + */ + protected void stopDaemon(String daemonName) { + new DaemonProxy(daemonName).stop(); + } + + /** * Returns the VPN profile associated with the connection. */ protected E getProfile() { @@ -104,8 +123,22 @@ abstract class VpnService<E extends VpnProfile> { } void setContext(VpnServiceBinder context, E profile) { - mContext = context; mProfile = profile; + recover(context); + } + + void recover(VpnServiceBinder context) { + mContext = context; + mNotification = new NotificationHelper(); + + if (VpnState.CONNECTED.equals(mState)) { + Log.i("VpnService", " recovered: " + mProfile.getName()); + new Thread(new Runnable() { + public void run() { + enterConnectivityLoop(); + } + }).start(); + } } VpnState getState() { @@ -117,14 +150,14 @@ abstract class VpnService<E extends VpnProfile> { mState = VpnState.CONNECTING; broadcastConnectivity(VpnState.CONNECTING); + stopPreviouslyRunDaemons(); String serverIp = getIp(getProfile().getServerName()); - + saveLocalIpAndInterface(serverIp); onBeforeConnect(); connect(serverIp, username, password); waitUntilConnectedOrTimedout(); return true; } catch (Throwable e) { - Log.e(TAG, "onConnect()", e); onError(e); return false; } @@ -132,7 +165,7 @@ abstract class VpnService<E extends VpnProfile> { synchronized void onDisconnect() { try { - Log.d(TAG, " disconnecting VPN..."); + Log.i(TAG, "disconnecting VPN..."); mState = VpnState.DISCONNECTING; broadcastConnectivity(VpnState.DISCONNECTING); mNotification.showDisconnect(); @@ -152,6 +185,7 @@ abstract class VpnService<E extends VpnProfile> { Log.w(TAG, " multiple errors occur, record the last one: " + error); } + Log.e(TAG, "onError()", error); mError = error; onDisconnect(); } @@ -161,16 +195,18 @@ abstract class VpnService<E extends VpnProfile> { } - private void onBeforeConnect() { + private void onBeforeConnect() throws IOException { mNotification.disableNotification(); - SystemProperties.set(VPN_DNS1, "-"); - SystemProperties.set(VPN_DNS2, "-"); + SystemProperties.set(VPN_DNS1, ""); + SystemProperties.set(VPN_DNS2, ""); SystemProperties.set(VPN_STATUS, VPN_IS_DOWN); - Log.d(TAG, " VPN UP: " + SystemProperties.get(VPN_STATUS)); + if (DBG) { + Log.d(TAG, " VPN UP: " + SystemProperties.get(VPN_STATUS)); + } } - private void waitUntilConnectedOrTimedout() { + private void waitUntilConnectedOrTimedout() throws IOException { sleep(2000); // 2 seconds for (int i = 0; i < 60; i++) { if (mState != VpnState.CONNECTING) { @@ -187,39 +223,49 @@ abstract class VpnService<E extends VpnProfile> { synchronized (VpnService.this) { if (mState == VpnState.CONNECTING) { - Log.d(TAG, " connecting timed out !!"); onError(new IOException("Connecting timed out")); } } } - private synchronized void onConnected() { - Log.d(TAG, "onConnected()"); + private synchronized void onConnected() throws IOException { + if (DBG) Log.d(TAG, "onConnected()"); mDaemonHelper.closeSockets(); - saveVpnDnsProperties(); + saveOriginalDns(); saveAndSetDomainSuffices(); mState = VpnState.CONNECTED; + mStartTime = System.currentTimeMillis(); + + // set DNS after saving the states in case the process gets killed + // before states are saved + saveSelf(); + setVpnDns(); broadcastConnectivity(VpnState.CONNECTED); enterConnectivityLoop(); } + private void saveSelf() throws IOException { + mContext.saveStates(); + } + private synchronized void onFinalCleanUp() { - Log.d(TAG, "onFinalCleanUp()"); + if (DBG) Log.d(TAG, "onFinalCleanUp()"); if (mState == VpnState.IDLE) return; // keep the notification when error occurs if (!anyError()) mNotification.disableNotification(); - restoreOriginalDnsProperties(); + restoreOriginalDns(); restoreOriginalDomainSuffices(); mState = VpnState.IDLE; broadcastConnectivity(VpnState.IDLE); // stop the service itself + mContext.removeStates(); mContext.stopSelf(); } @@ -227,46 +273,38 @@ abstract class VpnService<E extends VpnProfile> { return (mError != null); } - private void restoreOriginalDnsProperties() { + private void restoreOriginalDns() { // restore only if they are not overridden - if (mVpnDns1.equals(SystemProperties.get(DNS1))) { - Log.d(TAG, String.format("restore original dns prop: %s --> %s", + String vpnDns1 = SystemProperties.get(VPN_DNS1); + if (vpnDns1.equals(SystemProperties.get(DNS1))) { + Log.i(TAG, String.format("restore original dns prop: %s --> %s", SystemProperties.get(DNS1), mOriginalDns1)); - Log.d(TAG, String.format("restore original dns prop: %s --> %s", + Log.i(TAG, String.format("restore original dns prop: %s --> %s", SystemProperties.get(DNS2), mOriginalDns2)); SystemProperties.set(DNS1, mOriginalDns1); SystemProperties.set(DNS2, mOriginalDns2); } } - private void saveVpnDnsProperties() { - mOriginalDns1 = mOriginalDns2 = ""; - for (int i = 0; i < 5; i++) { - mVpnDns1 = SystemProperties.get(VPN_DNS1); - mVpnDns2 = SystemProperties.get(VPN_DNS2); - if (mOriginalDns1.equals(mVpnDns1)) { - Log.d(TAG, "wait for vpn dns to settle in..." + i); - sleep(200); - } else { - mOriginalDns1 = SystemProperties.get(DNS1); - mOriginalDns2 = SystemProperties.get(DNS2); - SystemProperties.set(DNS1, mVpnDns1); - SystemProperties.set(DNS2, mVpnDns2); - Log.d(TAG, String.format("save original dns prop: %s, %s", - mOriginalDns1, mOriginalDns2)); - Log.d(TAG, String.format("set vpn dns prop: %s, %s", - mVpnDns1, mVpnDns2)); - return; - } - } - Log.d(TAG, "saveVpnDnsProperties(): DNS not updated??"); - mOriginalDns1 = mVpnDns1 = SystemProperties.get(DNS1); - mOriginalDns2 = mVpnDns2 = SystemProperties.get(DNS2); + private void saveOriginalDns() { + mOriginalDns1 = SystemProperties.get(DNS1); + mOriginalDns2 = SystemProperties.get(DNS2); + Log.i(TAG, String.format("save original dns prop: %s, %s", + mOriginalDns1, mOriginalDns2)); + } + + private void setVpnDns() { + String vpnDns1 = SystemProperties.get(VPN_DNS1); + String vpnDns2 = SystemProperties.get(VPN_DNS2); + SystemProperties.set(DNS1, vpnDns1); + SystemProperties.set(DNS2, vpnDns2); + Log.i(TAG, String.format("set vpn dns prop: %s, %s", + vpnDns1, vpnDns2)); } private void saveAndSetDomainSuffices() { mOriginalDomainSuffices = SystemProperties.get(DNS_DOMAIN_SUFFICES); - Log.d(TAG, "save original dns search: " + mOriginalDomainSuffices); + Log.i(TAG, "save original suffices: " + mOriginalDomainSuffices); String list = mProfile.getDomainSuffices(); if (!TextUtils.isEmpty(list)) { SystemProperties.set(DNS_DOMAIN_SUFFICES, list); @@ -274,7 +312,7 @@ abstract class VpnService<E extends VpnProfile> { } private void restoreOriginalDomainSuffices() { - Log.d(TAG, "restore original dns search --> " + mOriginalDomainSuffices); + Log.i(TAG, "restore original suffices --> " + mOriginalDomainSuffices); SystemProperties.set(DNS_DOMAIN_SUFFICES, mOriginalDomainSuffices); } @@ -298,46 +336,73 @@ abstract class VpnService<E extends VpnProfile> { } private void enterConnectivityLoop() { - mStartTime = System.currentTimeMillis(); - - Log.d(TAG, " +++++ connectivity monitor running"); + Log.i(TAG, "VPN connectivity monitor running"); try { for (;;) { synchronized (VpnService.this) { - if (mState != VpnState.CONNECTED) break; + if (mState != VpnState.CONNECTED || !checkConnectivity()) { + break; + } mNotification.update(); - checkConnectivity(); + checkDns(); VpnService.this.wait(1000); // 1 second } } } catch (InterruptedException e) { - Log.e(TAG, "connectivity monitor", e); + onError(e); } - Log.d(TAG, " ----- connectivity monitor stopped"); + Log.i(TAG, "VPN connectivity monitor stopped"); } - private void checkConnectivity() { - if (mDaemonHelper.anyDaemonStopped() || isLocalIpChanged()) { - onDisconnect(); + private void saveLocalIpAndInterface(String serverIp) throws IOException { + DatagramSocket s = new DatagramSocket(); + int port = 80; // arbitrary + s.connect(InetAddress.getByName(serverIp), port); + InetAddress localIp = s.getLocalAddress(); + mLocalIp = localIp.getHostAddress(); + NetworkInterface localIf = NetworkInterface.getByInetAddress(localIp); + mLocalIf = (localIf == null) ? null : localIf.getName(); + if (TextUtils.isEmpty(mLocalIf)) { + throw new IOException("Local interface is empty!"); + } + if (DBG) { + Log.d(TAG, " Local IP: " + mLocalIp + ", if: " + mLocalIf); } } - private boolean isLocalIpChanged() { - // TODO - if (!isDnsIntact()) { - Log.w(TAG, " local IP changed"); - return true; - } else { + // returns false if vpn connectivity is broken + private boolean checkConnectivity() { + if (mDaemonHelper.anyDaemonStopped() || isLocalIpChanged()) { + onDisconnect(); return false; + } else { + return true; } } - private boolean isDnsIntact() { + private void checkDns() { String dns1 = SystemProperties.get(DNS1); - if (!mVpnDns1.equals(dns1)) { - Log.w(TAG, " dns being overridden by: " + dns1); - return false; - } else { + String vpnDns1 = SystemProperties.get(VPN_DNS1); + if (!dns1.equals(vpnDns1) && dns1.equals(mOriginalDns1)) { + // dhcp expires? + setVpnDns(); + } + } + + private boolean isLocalIpChanged() { + try { + InetAddress localIp = InetAddress.getByName(mLocalIp); + NetworkInterface localIf = + NetworkInterface.getByInetAddress(localIp); + if (localIf == null || !mLocalIf.equals(localIf.getName())) { + Log.w(TAG, " local If changed from " + mLocalIf + + " to " + localIf); + return true; + } else { + return false; + } + } catch (IOException e) { + Log.w(TAG, "isLocalIpChanged()", e); return true; } } @@ -349,7 +414,7 @@ abstract class VpnService<E extends VpnProfile> { } } - private class DaemonHelper { + private class DaemonHelper implements Serializable { private List<DaemonProxy> mDaemonList = new ArrayList<DaemonProxy>(); @@ -376,7 +441,7 @@ abstract class VpnService<E extends VpnProfile> { synchronized boolean anyDaemonStopped() { for (DaemonProxy s : mDaemonList) { if (s.isStopped()) { - Log.w(TAG, " daemon gone: " + s.getName()); + Log.w(TAG, " VPN daemon gone: " + s.getName()); return true; } } @@ -401,6 +466,14 @@ abstract class VpnService<E extends VpnProfile> { onError(VpnManager.VPN_ERROR_AUTH); return true; + case CHALLENGE_ERROR_CODE: + onError(VpnManager.VPN_ERROR_CHALLENGE); + return true; + + case REMOTE_HUNG_UP_ERROR_CODE: + onError(VpnManager.VPN_ERROR_REMOTE_HUNG_UP); + return true; + default: onError(VpnManager.VPN_ERROR_CONNECTION_FAILED); return true; diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java index 513a2c9..4892a7b 100644 --- a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java +++ b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java @@ -27,23 +27,31 @@ import android.net.vpn.VpnManager; import android.net.vpn.VpnProfile; import android.net.vpn.VpnState; import android.os.IBinder; +import android.util.Log; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; /** * The service class for managing a VPN connection. It implements the * {@link IVpnService} binder interface. */ public class VpnServiceBinder extends Service { - private final String TAG = VpnServiceBinder.class.getSimpleName(); + private static final String TAG = VpnServiceBinder.class.getSimpleName(); + private static final boolean DBG = true; + + private static final String STATES_FILE_PATH = "/data/misc/vpn/.states"; // The actual implementation is delegated to the VpnService class. private VpnService<? extends VpnProfile> mService; private final IBinder mBinder = new IVpnService.Stub() { public boolean connect(VpnProfile p, String username, String password) { - android.util.Log.d("VpnServiceBinder", "becoming foreground"); - setForeground(true); return VpnServiceBinder.this.connect(p, username, password); } @@ -57,6 +65,13 @@ public class VpnServiceBinder extends Service { }; @Override + public void onCreate() { + super.onCreate(); + checkSavedStates(); + } + + + @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); } @@ -66,14 +81,30 @@ public class VpnServiceBinder extends Service { return mBinder; } + void saveStates() throws IOException { + if (DBG) Log.d("VpnServiceBinder", " saving states"); + ObjectOutputStream oos = + new ObjectOutputStream(new FileOutputStream(STATES_FILE_PATH)); + oos.writeObject(mService); + oos.close(); + } + + void removeStates() { + try { + new File(STATES_FILE_PATH).delete(); + } catch (Throwable e) { + if (DBG) Log.d("VpnServiceBinder", " remove states: " + e); + } + } + private synchronized boolean connect(final VpnProfile p, final String username, final String password) { if (mService != null) return false; + final VpnService s = mService = createService(p); new Thread(new Runnable() { public void run() { - mService = createService(p); - mService.onConnect(username, password); + s.onConnect(username, password); } }).start(); return true; @@ -81,12 +112,11 @@ public class VpnServiceBinder extends Service { private synchronized void disconnect() { if (mService == null) return; + final VpnService s = mService; new Thread(new Runnable() { public void run() { - mService.onDisconnect(); - android.util.Log.d("VpnServiceBinder", "becoming background"); - setForeground(false); + s.onDisconnect(); } }).start(); } @@ -100,6 +130,21 @@ public class VpnServiceBinder extends Service { } } + private void checkSavedStates() { + try { + ObjectInputStream ois = new ObjectInputStream(new FileInputStream( + STATES_FILE_PATH)); + mService = (VpnService<? extends VpnProfile>) ois.readObject(); + mService.recover(this); + ois.close(); + } catch (FileNotFoundException e) { + // do nothing + } catch (Throwable e) { + Log.i("VpnServiceBinder", "recovery error, remove states: " + e); + removeStates(); + } + } + private VpnService<? extends VpnProfile> createService(VpnProfile p) { switch (p.getType()) { case L2TP: |