diff options
| -rwxr-xr-x | res/values/strings.xml | 4 | ||||
| -rw-r--r-- | src/de/blinkt/openvpn/LogWindow.java | 88 | ||||
| -rw-r--r-- | src/de/blinkt/openvpn/core/DeviceStateReceiver.java | 143 | ||||
| -rw-r--r-- | src/de/blinkt/openvpn/core/OpenVPN.java | 43 | ||||
| -rw-r--r-- | src/de/blinkt/openvpn/core/OpenVPNManagement.java | 10 | ||||
| -rw-r--r-- | src/de/blinkt/openvpn/core/OpenVpnManagementThread.java | 83 | ||||
| -rw-r--r-- | src/de/blinkt/openvpn/core/OpenVpnService.java | 73 | 
7 files changed, 282 insertions, 162 deletions
diff --git a/res/values/strings.xml b/res/values/strings.xml index e9bacaee..10074e8a 100755 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -281,5 +281,9 @@      <string name="screenoff_pause">Pausing connection in screen off state: less than %1$s in %2$ss</string>      <string name="screen_nopersistenttun">Warning: Persistent tun not enabled for this VPN. Traffic will use the normal Internet connection when the screen is off.</string>      <string name="save_password">Save Password</string> +    <string name="pauseVPN">Pause VPN</string> +    <string name="resumevpn">Resume VPN</string> +    <string name="state_userpause">VPN pause requested by user</string> +    <string name="state_screenoff">VPN paused - screen off</string>  </resources>
\ No newline at end of file diff --git a/src/de/blinkt/openvpn/LogWindow.java b/src/de/blinkt/openvpn/LogWindow.java index 5c78f2f6..1d75c154 100644 --- a/src/de/blinkt/openvpn/LogWindow.java +++ b/src/de/blinkt/openvpn/LogWindow.java @@ -1,21 +1,8 @@  package de.blinkt.openvpn; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.Vector; -  import android.app.AlertDialog; -import android.app.AlertDialog.Builder;  import android.app.ListActivity; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.ComponentName; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.content.Intent; -import android.content.ServiceConnection; +import android.content.*;  import android.database.DataSetObserver;  import android.os.Bundle;  import android.os.Handler; @@ -23,17 +10,9 @@ import android.os.Handler.Callback;  import android.os.IBinder;  import android.os.Message;  import android.text.format.DateFormat; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; +import android.view.*; +import android.widget.*;  import android.widget.AdapterView.OnItemLongClickListener; -import android.widget.ListAdapter; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.Toast;  import de.blinkt.openvpn.core.OpenVPN;  import de.blinkt.openvpn.core.OpenVPN.ConnectionStatus;  import de.blinkt.openvpn.core.OpenVPN.LogItem; @@ -43,6 +22,11 @@ import de.blinkt.openvpn.core.OpenVpnService;  import de.blinkt.openvpn.core.OpenVpnService.LocalBinder;  import de.blinkt.openvpn.core.ProfileManager; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Vector; +  public class LogWindow extends ListActivity implements StateListener  {  	private static final String LOGTIMEFORMAT = "logtimeformat";  	private static final int START_VPN_CONFIG = 0; @@ -259,28 +243,32 @@ public class LogWindow extends ListActivity implements StateListener  {  	private LogWindowListAdapter ladapter;  	private TextView mSpeedView; +    private void showDisconnectDialog(final OpenVpnService service) { +        AlertDialog.Builder builder = new AlertDialog.Builder(this); +        builder.setTitle(R.string.title_cancel); +        builder.setMessage(R.string.cancel_connection_query); +        builder.setNegativeButton(android.R.string.no, null); +        builder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { -	@Override +            @Override +            public void onClick(DialogInterface dialog, int which) { +                ProfileManager.setConntectedVpnProfileDisconnected(LogWindow.this); +                if(service.getManagement()!=null) +                    service.getManagement().stopVPN(); +            } +        }); + +        builder.show(); +    } + + +    @Override  	public boolean onOptionsItemSelected(MenuItem item) {  		if(item.getItemId()==R.id.clearlog) {  			ladapter.clearLog();  			return true;  		} else if(item.getItemId()==R.id.cancel){ -			Builder builder = new AlertDialog.Builder(this); -			builder.setTitle(R.string.title_cancel); -			builder.setMessage(R.string.cancel_connection_query); -			builder.setNegativeButton(android.R.string.no, null); -			builder.setPositiveButton(android.R.string.yes, new OnClickListener() { - -				@Override -				public void onClick(DialogInterface dialog, int which) { -					ProfileManager.setConntectedVpnProfileDisconnected(getApplicationContext()); -					if(mService.getManagement()!=null) -						mService.getManagement().stopVPN(); -				} -			}); - -			builder.show(); +            showDisconnectDialog(mService);  			return true;  		} else if(item.getItemId()==R.id.send) {  			ladapter.shareLog(); @@ -313,7 +301,7 @@ public class LogWindow extends ListActivity implements StateListener  {  	} -	@Override +    @Override  	public boolean onCreateOptionsMenu(Menu menu) {  		MenuInflater inflater = getMenuInflater();  		inflater.inflate(R.menu.logmenu, menu); @@ -325,9 +313,21 @@ public class LogWindow extends ListActivity implements StateListener  {  	protected void onResume() {  		super.onResume();  		OpenVPN.addStateListener(this); -	} -	@Override +        if (getIntent() !=null && OpenVpnService.DISCONNECT_VPN.equals(getIntent().getAction())) +            showDisconnectDialog(mService); + +        setIntent(null); + +    } + +    @Override +    protected void onNewIntent(Intent intent) { +        super.onNewIntent(intent); +        setIntent(intent); +    } + +    @Override  	protected void onActivityResult(int requestCode, int resultCode, Intent data) {  		if (requestCode == START_VPN_CONFIG && resultCode==RESULT_OK) {  			String configuredVPN = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID); @@ -402,7 +402,7 @@ public class LogWindow extends ListActivity implements StateListener  {  	} -	@Override +    @Override  	public void updateState(final String status,final String logmessage, final int resid, final ConnectionStatus level) {  		runOnUiThread(new Runnable() { diff --git a/src/de/blinkt/openvpn/core/DeviceStateReceiver.java b/src/de/blinkt/openvpn/core/DeviceStateReceiver.java index d5029b07..ecdf5c55 100644 --- a/src/de/blinkt/openvpn/core/DeviceStateReceiver.java +++ b/src/de/blinkt/openvpn/core/DeviceStateReceiver.java @@ -13,9 +13,11 @@ import de.blinkt.openvpn.core.OpenVPN.ByteCountListener;  import java.util.LinkedList;
 +import static de.blinkt.openvpn.core.OpenVPNManagement.pauseReason;
 +
  public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountListener {
 -    private int lastNetwork=-1;
 -    private OpenVPNManagement mManangement;
 +    private int lastNetwork = -1;
 +    private OpenVPNManagement mManagement;
      // Window time in s
      private final int TRAFFIC_WINDOW = 60;
 @@ -23,10 +25,11 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL      private final long TRAFFIC_LIMIT = 64 * 1024;
 -    connectState network= connectState.DISCONNECTED;
 +    connectState network = connectState.DISCONNECTED;
      connectState screen = connectState.SHOULDBECONNECTED;
 +    connectState userpause = connectState.SHOULDBECONNECTED;
 -    private String lastStateMsg=null;
 +    private String lastStateMsg = null;
      enum connectState {
          SHOULDBECONNECTED,
 @@ -35,8 +38,7 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL      }
      static class Datapoint {
 -        private Datapoint(long t, long d)
 -        {
 +        private Datapoint(long t, long d) {
              timestamp = t;
              data = d;
          }
 @@ -49,34 +51,49 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL      @Override
      public void updateByteCount(long in, long out, long diffin, long diffout) {
 -        if (screen!=connectState.PENDINGDISCONNECT)
 +        if (screen != connectState.PENDINGDISCONNECT)
              return;
          long total = diffin + diffout;
 -        trafficdata.add(new Datapoint(System.currentTimeMillis(),total));
 +        trafficdata.add(new Datapoint(System.currentTimeMillis(), total));
 -        while(trafficdata.getFirst().timestamp <= (System.currentTimeMillis() - TRAFFIC_WINDOW*1000)) {
 +        while (trafficdata.getFirst().timestamp <= (System.currentTimeMillis() - TRAFFIC_WINDOW * 1000)) {
              trafficdata.removeFirst();
          }
          long windowtraffic = 0;
 -        for (Datapoint dp: trafficdata)
 +        for (Datapoint dp : trafficdata)
              windowtraffic += dp.data;
 -		if(windowtraffic < TRAFFIC_LIMIT) {
 -			screen = connectState.DISCONNECTED;
 -			OpenVPN.logInfo(R.string.screenoff_pause,
 -					OpenVpnService.humanReadableByteCount(TRAFFIC_LIMIT, false), TRAFFIC_WINDOW);
 +        if (windowtraffic < TRAFFIC_LIMIT) {
 +            screen = connectState.DISCONNECTED;
 +            OpenVPN.logInfo(R.string.screenoff_pause,
 +                    OpenVpnService.humanReadableByteCount(TRAFFIC_LIMIT, false), TRAFFIC_WINDOW);
 -			mManangement.pause();
 -		}
 +            mManagement.pause(getPauseReason());
 +        }
      }
 +    public void userPause(boolean pause) {
 +        if (pause) {
 +            userpause = connectState.DISCONNECTED;
 +            // Check if we should disconnect
 +            mManagement.pause(getPauseReason());
 +        } else {
 +            boolean wereConnected = shouldBeConnected();
 +            userpause = connectState.SHOULDBECONNECTED;
 +            if (shouldBeConnected() && !wereConnected)
 +                mManagement.resume();
 +            else
 +                // Update the reason why we currently paused
 +                mManagement.pause(getPauseReason());
 +        }
 +    }
      public DeviceStateReceiver(OpenVPNManagement magnagement) {
          super();
 -        mManangement = magnagement;
 +        mManagement = magnagement;
      }
 @@ -85,31 +102,36 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL          SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
 -        if(ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
 +        if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
              networkStateChange(context);
          } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
 -            boolean screenoff = prefs.getBoolean("screenoff", false);
 +            boolean screenOff = prefs.getBoolean("screenoff", false);
 -            if(screenoff) {
 -                if(!ProfileManager.getLastConnectedVpn().mPersistTun)
 +            if (screenOff) {
 +                if (!ProfileManager.getLastConnectedVpn().mPersistTun)
                      OpenVPN.logError(R.string.screen_nopersistenttun);
                  screen = connectState.PENDINGDISCONNECT;
                  fillTrafficData();
 -                if (network == connectState.DISCONNECTED)
 +                if (network == connectState.DISCONNECTED || userpause == connectState.DISCONNECTED)
                      screen = connectState.DISCONNECTED;
              }
          } else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
              // Network was disabled because screen off
 -            if (screen == connectState.DISCONNECTED && network == connectState.SHOULDBECONNECTED) {
 -                mManangement.resume();
 -
 -            }
 +            boolean connected = shouldBeConnected();
              screen = connectState.SHOULDBECONNECTED;
 +            /* should be connected has changed because the screen is on now, connect the VPN */
 +            if (shouldBeConnected() != connected)
 +                mManagement.resume();
 +            else
 +                /*Update the reason why we are still paused */
 +                mManagement.pause(getPauseReason());
 +
          }
      }
 +
      private void fillTrafficData() {
          trafficdata.add(new Datapoint(System.currentTimeMillis(), TRAFFIC_LIMIT));
      }
 @@ -122,18 +144,18 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL          String netstatestring;
 -        if(networkInfo==null) {
 +        if (networkInfo == null) {
              netstatestring = "not connected";
 -        } else  {
 +        } else {
              String subtype = networkInfo.getSubtypeName();
 -            if(subtype==null)
 +            if (subtype == null)
                  subtype = "";
              String extrainfo = networkInfo.getExtraInfo();
 -            if(extrainfo==null)
 -                extrainfo="";
 +            if (extrainfo == null)
 +                extrainfo = "";
  			/*
 -			if(networkInfo.getType()==android.net.ConnectivityManager.TYPE_WIFI) {
 +            if(networkInfo.getType()==android.net.ConnectivityManager.TYPE_WIFI) {
  				WifiManager wifiMgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
  				WifiInfo wifiinfo = wifiMgr.getConnectionInfo();
  				extrainfo+=wifiinfo.getBSSID();
 @@ -142,52 +164,71 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL  			}*/
 -
 -            netstatestring = String.format("%2$s %4$s to %1$s %3$s",networkInfo.getTypeName(),
 -                    networkInfo.getDetailedState(),extrainfo,subtype );
 +            netstatestring = String.format("%2$s %4$s to %1$s %3$s", networkInfo.getTypeName(),
 +                    networkInfo.getDetailedState(), extrainfo, subtype);
          }
 -        if(networkInfo!=null && networkInfo.getState() == State.CONNECTED) {
 +        if (networkInfo != null && networkInfo.getState() == State.CONNECTED) {
              int newnet = networkInfo.getType();
              network = connectState.SHOULDBECONNECTED;
 -            if(sendusr1 && lastNetwork!=newnet) {
 +            if (sendusr1 && lastNetwork != newnet) {
                  if (screen == connectState.PENDINGDISCONNECT)
                      screen = connectState.DISCONNECTED;
 -                if (lastNetwork==-1){
 -                    if (screen == connectState.SHOULDBECONNECTED)
 -                        mManangement.resume();
 -                }else{
 -                    if (screen == connectState.SHOULDBECONNECTED)
 -                        mManangement.reconnect();
 +                if (shouldBeConnected()) {
 +                    if (lastNetwork == -1) {
 +                        mManagement.resume();
 +                    } else {
 +                        mManagement.reconnect();
 +                    }
                  }
                  lastNetwork = newnet;
              }
 -        } else if (networkInfo==null) {
 +        } else if (networkInfo == null) {
              // Not connected, stop openvpn, set last connected network to no network
 -            lastNetwork=-1;
 -            if(sendusr1) {
 -                mManangement.pause();
 +            lastNetwork = -1;
 +            if (sendusr1) {
                  network = connectState.DISCONNECTED;
 -                // Set screen state to be disconnected if it want to disconnect
 +                // Set screen state to be disconnected if disconnect pending
                  if (screen == connectState.PENDINGDISCONNECT)
                      screen = connectState.DISCONNECTED;
 +
 +                mManagement.pause(getPauseReason());
              }
          }
 -        if(!netstatestring.equals(lastStateMsg))
 +        if (!netstatestring.equals(lastStateMsg))
              OpenVPN.logInfo(R.string.netstatus, netstatestring);
 -        lastStateMsg=netstatestring;
 +        lastStateMsg = netstatestring;
 +
 +    }
 +
 +    public boolean isUserPaused() {
 +        return userpause == connectState.DISCONNECTED;
 +    }
 +
 +    private boolean shouldBeConnected() {
 +        return (screen == connectState.SHOULDBECONNECTED && userpause == connectState.SHOULDBECONNECTED &&
 +                network == connectState.SHOULDBECONNECTED);
 +    }
 +
 +    private pauseReason getPauseReason() {
 +        if (userpause == connectState.DISCONNECTED)
 +            return pauseReason.userPause;
 +
 +        if (screen == connectState.DISCONNECTED)
 +            return pauseReason.screenOff;
 +        return pauseReason.noNetwork;
      }
      private NetworkInfo getCurrentNetworkInfo(Context context) {
 -        ConnectivityManager conn =  (ConnectivityManager)
 +        ConnectivityManager conn = (ConnectivityManager)
                  context.getSystemService(Context.CONNECTIVITY_SERVICE);
          return conn.getActiveNetworkInfo();
 diff --git a/src/de/blinkt/openvpn/core/OpenVPN.java b/src/de/blinkt/openvpn/core/OpenVPN.java index 450a13ea..66d985bd 100644 --- a/src/de/blinkt/openvpn/core/OpenVPN.java +++ b/src/de/blinkt/openvpn/core/OpenVPN.java @@ -41,21 +41,17 @@ public class OpenVPN { -	public enum ConnectionStatus { -		LEVEL_NONETWORK (3), -		LEVEL_NOTCONNECTED  (4), -		LEVEL_AUTH_FAILED (5), -		LEVEL_WAITING_FOR_USER_INPUT (6), -		LEVEL_CONNECTING_SERVER_REPLIED ( 1), -		LEVEL_CONNECTING_NO_SERVER_REPLY_YET (2), -		LEVEL_CONNECTED (0), UNKNOWN_LEVEL(-1); - -		private final int level; - -		ConnectionStatus(int level){ -			this.level = level; -		} -	} +    public enum ConnectionStatus { +        LEVEL_CONNECTED, +        LEVEL_VPNPAUSED, +        LEVEL_CONNECTING_SERVER_REPLIED, +        LEVEL_CONNECTING_NO_SERVER_REPLY_YET, +        LEVEL_NONETWORK, +		LEVEL_NOTCONNECTED, +		LEVEL_AUTH_FAILED, +		LEVEL_WAITING_FOR_USER_INPUT, +		UNKNOWN_LEVEL +    }  	public static final byte[] officalkey = {-58, -42, -44, -106, 90, -88, -87, -88, -52, -124, 84, 117, 66, 79, -112, -111, -46, 86, -37, 109};  	public static final byte[] officaldebugkey = {-99, -69, 45, 71, 114, -116, 82, 66, -99, -122, 50, -70, -56, -111, 98, -35, -65, 105, 82, 43}; @@ -305,7 +301,22 @@ public class OpenVPN {  	} -	private static ConnectionStatus getLevel(String state){ +    public static void updateStatePause(OpenVPNManagement.pauseReason pauseReason) { +        switch (pauseReason) { +            case noNetwork: +                OpenVPN.updateStateString("NONETWORK", "", R.string.state_nonetwork, ConnectionStatus.LEVEL_NONETWORK); +                break; +            case screenOff: +                OpenVPN.updateStateString("SCREENOFF", "", R.string.state_screenoff, ConnectionStatus.LEVEL_VPNPAUSED); +                break; +            case userPause: +                OpenVPN.updateStateString("USERPAUSE", "", R.string.state_userpause, ConnectionStatus.LEVEL_VPNPAUSED); +                break; +        } + +    } + +    private static ConnectionStatus getLevel(String state){  		String[] noreplyet = {"CONNECTING","WAIT", "RECONNECTING", "RESOLVE", "TCP_CONNECT"};   		String[] reply = {"AUTH","GET_CONFIG","ASSIGN_IP","ADD_ROUTES"};  		String[] connected = {"CONNECTED"}; diff --git a/src/de/blinkt/openvpn/core/OpenVPNManagement.java b/src/de/blinkt/openvpn/core/OpenVPNManagement.java index 5b8fc074..ce8d38c2 100644 --- a/src/de/blinkt/openvpn/core/OpenVPNManagement.java +++ b/src/de/blinkt/openvpn/core/OpenVPNManagement.java @@ -1,11 +1,17 @@  package de.blinkt.openvpn.core;  public interface OpenVPNManagement { -	int mBytecountinterval=2; +    enum pauseReason { +        noNetwork, +        userPause, +        screenOff +    } + +	int mBytecountInterval =2;  	void reconnect(); -	void pause(); +	void pause(pauseReason reason);  	void resume(); diff --git a/src/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/src/de/blinkt/openvpn/core/OpenVpnManagementThread.java index 13bebc8a..c4b4f379 100644 --- a/src/de/blinkt/openvpn/core/OpenVpnManagementThread.java +++ b/src/de/blinkt/openvpn/core/OpenVpnManagementThread.java @@ -1,16 +1,5 @@  package de.blinkt.openvpn.core;
 -import java.io.FileDescriptor;
 -import java.io.IOException;
 -import java.io.InputStream;
 -import java.lang.reflect.InvocationTargetException;
 -import java.lang.reflect.Method;
 -import java.net.InetSocketAddress;
 -import java.net.SocketAddress;
 -import java.util.LinkedList;
 -import java.util.Locale;
 -import java.util.Vector;
 -
  import android.content.Context;
  import android.content.SharedPreferences;
  import android.net.LocalServerSocket;
 @@ -23,6 +12,18 @@ import de.blinkt.openvpn.R;  import de.blinkt.openvpn.VpnProfile;
  import de.blinkt.openvpn.core.OpenVPN.ConnectionStatus;
 +import java.io.FileDescriptor;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.lang.reflect.InvocationTargetException;
 +import java.lang.reflect.Method;
 +import java.net.InetSocketAddress;
 +import java.net.SocketAddress;
 +import java.util.Collections;
 +import java.util.LinkedList;
 +import java.util.Locale;
 +import java.util.Vector;
 +
  public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
  	private static final String TAG = "openvpn";
 @@ -33,11 +34,13 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {      private LocalServerSocket mServerSocket;
  	private boolean mReleaseHold=true;
  	private boolean mWaitingForRelease=false;
 -	private long mLastHoldRelease=0; 
 +	private long mLastHoldRelease=0;
  	private static Vector<OpenVpnManagementThread> active=new Vector<OpenVpnManagementThread>();
      private LocalSocket mServerSocketLocal;
 +    private pauseReason lastPauseReason = pauseReason.noNetwork;
 +
      public OpenVpnManagementThread(VpnProfile profile, OpenVpnService openVpnService) {
  		mProfile = profile;
  		mOpenVPNService = openVpnService;
 @@ -50,6 +53,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {  	}
 +
      public boolean openManagementInterface(Context c) {
          // Could take a while to open connection
          int tries=8;
 @@ -66,7 +70,9 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {              } catch (IOException e) {
                  // wait 300 ms before retrying
                  try { Thread.sleep(300);
 -                } catch (InterruptedException e1) {}
 +                } catch (InterruptedException e1) {
 +                    e1.printStackTrace();
 +                }
              }
              tries--;
 @@ -133,11 +139,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {  					e.printStackTrace();
  				}
  				if(fds!=null){
 -
 -					for (FileDescriptor fd : fds) {
 -
 -						mFDList.add(fd);
 -					}
 +                    Collections.addAll(mFDList, fds);
  				}
  				String input = new String(buffer,0,numbytesread,"UTF-8");
 @@ -157,7 +159,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {  	//! Hack O Rama 2000!
  	private void protectFileDescriptor(FileDescriptor fd) {
 -		Exception exp=null;
 +		Exception exp;
  		try {
  			Method getInt = FileDescriptor.class.getDeclaredMethod("getInt$");
  			int fdint = (Integer) getInt.invoke(fd);
 @@ -181,11 +183,10 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {  		} catch (NullPointerException e) {
  			exp =e;
  		}
 -		if(exp!=null) {
 -			exp.printStackTrace();
 -			Log.d("Openvpn", "Failed to retrieve fd from socket: " + fd);
 -			OpenVPN.logMessage(0, "",  "Failed to retrieve fd from socket: " + exp.getLocalizedMessage());
 -		}
 +
 +        exp.printStackTrace();
 +        Log.d("Openvpn", "Failed to retrieve fd from socket: " + fd);
 +        OpenVPN.logMessage(0, "", "Failed to retrieve fd from socket: " + exp.getLocalizedMessage());
  	}
  	private String processInput(String pendingInput) {
 @@ -214,6 +215,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {  			if(cmd.equals("INFO")) {
  				// Ignore greeting from mgmt
  				//logStatusMessage(command);
 +                ;
  			}else if (cmd.equals("PASSWORD")) {
  				processPWCommand(argument);
  			} else if (cmd.equals("HOLD")) {
 @@ -239,6 +241,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {  				Log.i(TAG, "Got unrecognized command" + command);
  			}
  		} else if (command.startsWith("SUCCESS:")) {
 +            ;
  			// ignore
  		} else {
  			Log.i(TAG, "Got unrecognized line from managment" + command);
 @@ -250,20 +253,25 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {  			releaseHoldCmd();
  		} else { 
  			mWaitingForRelease=true;
 -			OpenVPN.updateStateString("NONETWORK", "",R.string.state_nonetwork,ConnectionStatus.LEVEL_NONETWORK);
 +
 +            OpenVPN.updateStatePause(lastPauseReason);
 +
 +
  		}
  	}
  	private void releaseHoldCmd() {
  		if ((System.currentTimeMillis()- mLastHoldRelease) < 5000) {
  			try {
  				Thread.sleep(3000);
 -			} catch (InterruptedException e) {}
 +			} catch (InterruptedException e) {
 +                e.printStackTrace();
 +            }
  		}
  		mWaitingForRelease=false;
  		mLastHoldRelease  = System.currentTimeMillis();
  		managmentCommand("hold release\n");
 -		managmentCommand("bytecount " + mBytecountinterval + "\n");
 +		managmentCommand("bytecount " + mBytecountInterval + "\n");
  		managmentCommand("state on\n");
  	}
 @@ -366,7 +374,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {  	}
  	private boolean sendTunFD (String needed, String extra) {
 -		Exception exp = null;
 +		Exception exp;
  		if(!extra.equals("tun")) {
  			// We only support tun
  			String errmsg = String.format("Devicetype %s requested, but only tun is possible with the Android API, sorry!",extra);
 @@ -413,11 +421,10 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {  		} catch (IOException e) {
  			exp =e;
  		}
 -		if(exp!=null) {
 -			OpenVPN.logMessage(0,"", "Could not send fd over socket:" + exp.getLocalizedMessage());
 -			exp.printStackTrace();
 -		}
 -		return false;
 +        OpenVPN.logMessage(0, "", "Could not send fd over socket:" + exp.getLocalizedMessage());
 +        exp.printStackTrace();
 +
 +        return false;
  	}
  	private void processPWCommand(String argument) {
 @@ -485,8 +492,13 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {  	public void signalusr1() {
  		mReleaseHold=false;
 +
  		if(!mWaitingForRelease)
  			managmentCommand("signal SIGUSR1\n");
 +        else
 +            // If signalusr1 is called update the state string
 +            // if there is another for stopping
 +            OpenVPN.updateStatePause(lastPauseReason);
  	}
  	public void reconnect() {
 @@ -503,13 +515,16 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {  	}
  	@Override
 -	public void pause() {
 +	public void pause (pauseReason reason) {
 +        lastPauseReason = reason;
  		signalusr1();
  	}
  	@Override
  	public void resume() {
  		releaseHold();
 +        /* Reset the reason why we are disconnected */
 +        lastPauseReason = pauseReason.noNetwork;
  	}
  	@Override
 diff --git a/src/de/blinkt/openvpn/core/OpenVpnService.java b/src/de/blinkt/openvpn/core/OpenVpnService.java index 74ae7245..230ddbaa 100644 --- a/src/de/blinkt/openvpn/core/OpenVpnService.java +++ b/src/de/blinkt/openvpn/core/OpenVpnService.java @@ -9,7 +9,8 @@ import android.content.Context;  import android.content.Intent;  import android.content.IntentFilter;  import android.content.SharedPreferences; -import android.net.*; +import android.net.ConnectivityManager; +import android.net.VpnService;  import android.os.*;  import android.os.Handler.Callback;  import android.preference.PreferenceManager; @@ -33,8 +34,12 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac  	public static final String START_SERVICE_STICKY = "de.blinkt.openvpn.START_SERVICE_STICKY";  	public static final String ALWAYS_SHOW_NOTIFICATION = "de.blinkt.openvpn.NOTIFICATION_ALWAYS_VISIBLE"; +    public static final String DISCONNECT_VPN = "de.blinkt.openvpn.DISCONNECT_VPN"; +    private static final String PAUSE_VPN = "de.blinkt.openvpn.PAUSE_VPN"; +    private static final String RESUME_VPN = "de.blinkt.openvpn.RESUME_VPN"; -	private Thread mProcessThread=null; + +    private Thread mProcessThread=null;  	private final Vector<String> mDnslist=new Vector<String>(); @@ -60,8 +65,6 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac  	private static final int OPENVPN_STATUS = 1; -	public static final int PROTECT_FD = 0; -  	private static boolean mNotificationAlwaysVisible =false;  	private final IBinder mBinder = new LocalBinder(); @@ -111,12 +114,12 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac  		}  	} -	private void showNotification(String msg, String tickerText, boolean lowpriority, long when, ConnectionStatus level) { +	private void showNotification(String msg, String tickerText, boolean lowpriority, long when, ConnectionStatus status) {  		String ns = Context.NOTIFICATION_SERVICE;  		NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns); -		int icon = getIconByLevel(level); +		int icon = getIconByConnectionStatus(status);  		android.app.Notification.Builder nbuilder = new Notification.Builder(this); @@ -131,6 +134,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac  		nbuilder.setContentIntent(getLogPendingIntent());  		nbuilder.setSmallIcon(icon); +  		if(when !=0)  			nbuilder.setWhen(when); @@ -148,7 +152,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac  		startForeground(OPENVPN_STATUS, notification);  	} -    private int getIconByLevel(ConnectionStatus level) { +    private int getIconByConnectionStatus(ConnectionStatus level) {         switch (level) {             case LEVEL_CONNECTED:             case UNKNOWN_LEVEL: @@ -162,6 +166,8 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac                 return R.drawable.ic_stat_vpn_outline;             case LEVEL_CONNECTING_SERVER_REPLIED:                 return R.drawable.ic_stat_vpn_empty_halo; +           case LEVEL_VPNPAUSED: +               return android.R.drawable.ic_media_pause;             default:                 return R.drawable.ic_stat_vpn; @@ -181,17 +187,39 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac  				Method setUsesChronometer = nbuilder.getClass().getMethod("setUsesChronometer", boolean.class);  				setUsesChronometer.invoke(nbuilder,true); -				/*				PendingIntent cancelconnet=null; - -				nbuilder.addAction(android.R.drawable.ic_menu_close_clear_cancel,  -						getString(R.string.cancel_connection),cancelconnet); */  			} -			//ignore exception +            Intent disconnectVPN = new Intent(this,LogWindow.class); +            disconnectVPN.setAction(DISCONNECT_VPN); +            PendingIntent disconnectPendingIntent = PendingIntent.getActivity(this, 0, disconnectVPN, 0); + +            nbuilder.addAction(android.R.drawable.ic_menu_close_clear_cancel, +                    getString(R.string.cancel_connection),disconnectPendingIntent); + +            Intent pauseVPN = new Intent(this,OpenVpnService.class); +            if (mDeviceStateReceiver == null || !mDeviceStateReceiver.isUserPaused()) { +                pauseVPN.setAction(PAUSE_VPN); +                PendingIntent pauseVPNPending = PendingIntent.getService(this,0,pauseVPN,0); +                nbuilder.addAction(android.R.drawable.ic_media_pause, +                        getString(R.string.pauseVPN), pauseVPNPending); + +            } else { +                pauseVPN.setAction(RESUME_VPN); +                PendingIntent resumeVPNPending = PendingIntent.getService(this,0,pauseVPN,0); +                nbuilder.addAction(android.R.drawable.ic_media_play, +                        getString(R.string.resumevpn), resumeVPNPending); +            } + + +            //ignore exception  		} catch (NoSuchMethodException nsm) { +            nsm.printStackTrace();  		} catch (IllegalArgumentException e) { +            e.printStackTrace();  		} catch (IllegalAccessException e) { +            e.printStackTrace();  		} catch (InvocationTargetException e) { +            e.printStackTrace();  		}  	} @@ -241,7 +269,22 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac  		OpenVPN.addStateListener(this);  		OpenVPN.addByteCountListener(this); -		if(intent != null && intent.getAction() !=null &&intent.getAction().equals(START_SERVICE)) +        if(intent != null && intent.getAction() !=null &&intent.getAction().equals(PAUSE_VPN)) +        { +            if(mDeviceStateReceiver!=null) +                mDeviceStateReceiver.userPause(true); +            return START_NOT_STICKY; +        } + +        if(intent != null && intent.getAction() !=null &&intent.getAction().equals(RESUME_VPN)) +        { +            if(mDeviceStateReceiver!=null) +                mDeviceStateReceiver.userPause(false); +            return START_NOT_STICKY; +        } + + +        if(intent != null && intent.getAction() !=null &&intent.getAction().equals(START_SERVICE))  			return START_NOT_STICKY;  		if(intent != null && intent.getAction() !=null &&intent.getAction().equals(START_SERVICE_STICKY)) {  			return START_REDELIVER_INTENT; @@ -566,9 +609,9 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac  		if(mDisplayBytecount) {  			String netstat = String.format(getString(R.string.statusline_bytecount),  					humanReadableByteCount(in, false), -					humanReadableByteCount(diffin/ OpenVPNManagement.mBytecountinterval, true), +					humanReadableByteCount(diffin/ OpenVPNManagement.mBytecountInterval, true),  					humanReadableByteCount(out, false), -					humanReadableByteCount(diffout/ OpenVPNManagement.mBytecountinterval, true)); +					humanReadableByteCount(diffout/ OpenVPNManagement.mBytecountInterval, true));  			boolean lowpriority = !mNotificationAlwaysVisible;  			showNotification(netstat,null,lowpriority,mConnecttime, LEVEL_CONNECTED);  | 
