diff options
| author | Sean Leonard <meanderingcode@aetherislands.net> | 2013-06-09 04:31:27 -0600 | 
|---|---|---|
| committer | Sean Leonard <meanderingcode@aetherislands.net> | 2013-06-20 18:46:39 -0600 | 
| commit | 389dfcdfad555feb1cf212ef9b42626633d5eade (patch) | |
| tree | 4485c33699424bbb20f94e610171fa1ef01b08e2 /src | |
| parent | 33338d43e0e83329a7c46807e096b8148e19aff7 (diff) | |
Better control and UI feedback for VPN
Diffstat (limited to 'src')
| -rw-r--r-- | src/se/leap/leapclient/ConfigHelper.java | 4 | ||||
| -rw-r--r-- | src/se/leap/leapclient/Dashboard.java | 138 | ||||
| -rw-r--r-- | src/se/leap/leapclient/EIP.java | 140 | ||||
| -rw-r--r-- | src/se/leap/openvpn/LaunchVPN.java | 20 | ||||
| -rw-r--r-- | src/se/leap/openvpn/OpenVpnService.java | 15 | 
5 files changed, 308 insertions, 9 deletions
| diff --git a/src/se/leap/leapclient/ConfigHelper.java b/src/se/leap/leapclient/ConfigHelper.java index 1c2a482..cab5fde 100644 --- a/src/se/leap/leapclient/ConfigHelper.java +++ b/src/se/leap/leapclient/ConfigHelper.java @@ -73,7 +73,9 @@ public class ConfigHelper {      PASSWORD_KEY = "password",      ALLOW_REGISTRATION_KEY = "allow_registration",      EIP_SERVICE_API_PATH = "config/eip-service.json", -    ERRORS_KEY = "errors" +    ERRORS_KEY = "errors", +    RECEIVER_TAG = "receiverTag", +    REQUEST_TAG = "requestTag";      ;      final public static String NG_1024 = diff --git a/src/se/leap/leapclient/Dashboard.java b/src/se/leap/leapclient/Dashboard.java index 31719d9..f10966b 100644 --- a/src/se/leap/leapclient/Dashboard.java +++ b/src/se/leap/leapclient/Dashboard.java @@ -8,6 +8,8 @@ import org.json.JSONObject;  import se.leap.leapclient.ProviderAPIResultReceiver.Receiver;  import se.leap.openvpn.AboutFragment;  import se.leap.openvpn.MainActivity; +import se.leap.openvpn.OpenVPN; +import se.leap.openvpn.OpenVPN.StateListener;  import android.app.Activity;  import android.app.AlertDialog;  import android.app.DialogFragment; @@ -19,6 +21,7 @@ import android.content.Intent;  import android.content.SharedPreferences;  import android.os.Bundle;  import android.os.Handler; +import android.os.ResultReceiver;  import android.view.Menu;  import android.view.MenuItem;  import android.view.View; @@ -30,7 +33,7 @@ import android.widget.Switch;  import android.widget.TextView;  import android.widget.Toast; -public class Dashboard extends Activity implements LogInDialog.LogInDialogInterface, Receiver { +public class Dashboard extends Activity implements LogInDialog.LogInDialogInterface,Receiver,StateListener {  	protected static final int CONFIGURE_LEAP = 0; @@ -40,8 +43,14 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf  	private TextView providerNameTV;  	private TextView eipTypeTV; +	private Switch eipSwitch; +	private View eipDetail; +	private TextView eipStatus; +	 +	private boolean mEipWait = false;      public ProviderAPIResultReceiver providerAPI_result_receiver; +    private EIPReceiver mEIPReceiver;  	@Override  	protected void onCreate(Bundle savedInstanceState) { @@ -62,6 +71,24 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf  		else  			startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);  	} + +	@Override +	protected void onStop() { +		super.onStop(); +		// TODO null provider should only happen before ConfigurationWizard has run, once...better way? +		if (provider != null) +			if (provider.hasEIP() && provider.getEIPType() == "OpenVPN") +				OpenVPN.removeStateListener(this); +	} +	 +	@Override +	protected void onResume() { +		super.onResume(); +		// TODO null provider should only happen before ConfigurationWizard has run, once...better way? +		if (provider != null) +			if (provider.hasEIP() && provider.getEIPType() == "OpenVPN") +				OpenVPN.addStateListener(this); +	}  	@Override  	protected void onActivityResult(int requestCode, int resultCode, Intent data){ @@ -111,36 +138,55 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf  		if ( provider.hasEIP() /*&& provider.getEIP() != null*/){  			// FIXME let's schedule this, so we're not doing it when we load the app  			startService( new Intent(EIP.ACTION_UPDATE_EIP_SERVICE) ); +			if (provider.getEIPType() == "OpenVPN") +				OpenVPN.addStateListener(this);  			serviceItemEIP();  		}  	}  	private void serviceItemEIP() { +		mEIPReceiver = new EIPReceiver(new Handler()); +		mEIPReceiver.setReceiver(this); + +		Intent intent = new Intent(this,EIP.class); +		intent.setAction(EIP.ACTION_IS_EIP_RUNNING); +		intent.putExtra(ConfigHelper.RECEIVER_TAG, mEIPReceiver); +		startService(intent); +		  		((ViewStub) findViewById(R.id.eipOverviewStub)).inflate();  		// Set our EIP type title  		eipTypeTV = (TextView) findViewById(R.id.eipType);  		eipTypeTV.setText(provider.getEIPType()); +		  		// Show our EIP detail -		View eipDetail = ((RelativeLayout) findViewById(R.id.eipDetail)); +		eipDetail = ((RelativeLayout) findViewById(R.id.eipDetail));  		View eipSettings = findViewById(R.id.eipSettings);  		eipSettings.setVisibility(View.GONE); // FIXME too!  		eipDetail.setVisibility(View.VISIBLE); +		eipStatus = (TextView) findViewById(R.id.eipStatus);  		// TODO Bind our switch to run our EIP  		// What happens when our VPN stops running?  does it call the listener? -		Switch eipSwitch = (Switch) findViewById(R.id.eipSwitch); +		eipSwitch = (Switch) findViewById(R.id.eipSwitch);  		eipSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {  			@Override  			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { +				if (!mEipWait){ +					// We're gonna have to have some patience! +					buttonView.setClickable(false); +					mEipWait = true; +					  					Intent vpnIntent;  					if (isChecked){  						vpnIntent = new Intent(EIP.ACTION_START_EIP);  					} else {  						vpnIntent = new Intent(EIP.ACTION_STOP_EIP);  					} +					vpnIntent.putExtra(ConfigHelper.RECEIVER_TAG, mEIPReceiver);  					startService(vpnIntent);  				} +			}  		});  		//TODO write our info into the view	fragment that will expand with details and a settings button @@ -333,4 +379,90 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf  	public static Context getAppContext() {  		return app;  	} +	 +	@Override +	public void updateState(final String state, final String logmessage, final int localizedResId) { +		// Note: "states" are not organized anywhere...collected state strings: +		//		NOPROCESS,NONETWORK,BYTECOUNT,AUTH_FAILED + some parsing thing ( WAIT(?),AUTH,GET_CONFIG,ASSIGN_IP,CONNECTED(?) ) +		// TODO follow-back calls to updateState to find set variable values passed as first param & third param (find those strings...are they all R.string.STATE_* ?) +		runOnUiThread(new Runnable() { + +			@Override +			public void run() { +				if (eipStatus != null) { +					String prefix = getString(localizedResId) + ":"; +					if (state.equals("BYTECOUNT") || state.equals("NOPROCESS")) +						prefix = ""; +					eipStatus.setText(prefix + logmessage); +				} +			} +		}); +	} + +	protected class EIPReceiver extends ResultReceiver { +		 +		Dashboard mDashboard; +		 +		protected EIPReceiver(Handler handler){ +			super(handler); +		} +		 +		public void setReceiver(Dashboard receiver) { +			mDashboard = receiver; +		} + +		@Override +		protected void onReceiveResult(int resultCode, Bundle resultData) { +			super.onReceiveResult(resultCode, resultData); +			 +			// What were we asking for, again? +			String request = resultData.getString(ConfigHelper.REQUEST_TAG); +			// Should the EIP switch be on? +			mEipWait = true; +			boolean checked = false; +			 +			if (request == EIP.ACTION_IS_EIP_RUNNING) { +				switch (resultCode){ +				case RESULT_OK: +					checked = true; +					break; +				case RESULT_CANCELED: +					checked = false; +					break; +				} +			} else if (request == EIP.ACTION_START_EIP) { +				switch (resultCode){ +				case RESULT_OK: +					checked = true; +					break; +				case RESULT_CANCELED: +					checked = false; +					break; +				} +			} else if (request == EIP.ACTION_STOP_EIP) { +				switch (resultCode){ +				case RESULT_OK: +					checked = false; +					break; +				case RESULT_CANCELED: +					checked = true; +					break; +				} +			} else if (request == EIP.EIP_NOTIFICATION) { +				switch  (resultCode){ +				case RESULT_OK: +					checked = true; +					break; +				case RESULT_CANCELED: +					checked = false; +					break; +				} +			} +			 +			Switch eipS = ((Switch) mDashboard.findViewById(R.id.eipSwitch)); +			eipS.setChecked(checked); +			eipS.setClickable(true); +			mEipWait = false; +		} +	}  } diff --git a/src/se/leap/leapclient/EIP.java b/src/se/leap/leapclient/EIP.java index 21cbdfd..867805b 100644 --- a/src/se/leap/leapclient/EIP.java +++ b/src/se/leap/leapclient/EIP.java @@ -13,12 +13,21 @@ import org.json.JSONObject;  import se.leap.openvpn.ConfigParser;  import se.leap.openvpn.ConfigParser.ConfigParseError;  import se.leap.openvpn.LaunchVPN; -import se.leap.openvpn.OpenVpnManagementThread; +import se.leap.openvpn.OpenVpnService; +import se.leap.openvpn.OpenVpnService.LocalBinder;  import se.leap.openvpn.ProfileManager;  import se.leap.openvpn.VpnProfile; + +import android.app.Activity;  import android.app.IntentService; +import android.content.ComponentName;  import android.content.Context;  import android.content.Intent; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.IBinder; +import android.os.ResultReceiver;  import android.util.Log;  /** @@ -31,7 +40,17 @@ public final class EIP extends IntentService {  	public final static String ACTION_STOP_EIP = "se.leap.leapclient.STOP_EIP";  	public final static String ACTION_UPDATE_EIP_SERVICE = "se.leap.leapclient.UPDATE_EIP_SERVICE"; +	public final static String ACTION_IS_EIP_RUNNING = "se.leap.leapclient.IS_RUNNING"; +	 +	public final static String EIP_NOTIFICATION = "EIP_NOTIFICATION"; +	  	private static Context context; +	private static ResultReceiver mReceiver; +	// Binder to OpenVpnService for comm ops +	private static OpenVpnService mVpnService; +	private static boolean mBound = false; +	// Used to store actions to "resume" onServiceConnection +	private static String mPending = null;  	// Represents our Provider's eip-service.json  	private static JSONObject eipDefinition = null; @@ -56,13 +75,27 @@ public final class EIP extends IntentService {  			// TODO Auto-generated catch block  			e.printStackTrace();  		} +		 +		this.retreiveVpnService(); +	} +	 +	@Override +	public void onDestroy() { +		unbindService(mVpnServiceConn); +		mBound = false; + +		super.onDestroy();  	}  	@Override  	protected void onHandleIntent(Intent intent) {  		// Get our action from the Intent  		String action = intent.getAction(); +		// Get the ResultReceiver, if any +		mReceiver = intent.getParcelableExtra(ConfigHelper.RECEIVER_TAG); +		if ( action == ACTION_IS_EIP_RUNNING ) +			this.isRunning();  		if ( action == ACTION_UPDATE_EIP_SERVICE )  			this.updateEIPService();  		else if ( action == ACTION_START_EIP ) @@ -70,6 +103,70 @@ public final class EIP extends IntentService {  		else if ( action == ACTION_STOP_EIP )  			this.stopEIP();  	} +	 +	private void retreiveVpnService() { +		Intent bindIntent = new Intent(this,OpenVpnService.class); +		bindIntent.setAction(OpenVpnService.RETRIEVE_SERVICE); +		bindService(bindIntent, mVpnServiceConn, 0); +	} +	 +	private static ServiceConnection mVpnServiceConn = new ServiceConnection() { + +		@Override +		public void onServiceConnected(ComponentName name, IBinder service) { +			LocalBinder binder = (LocalBinder) service; +			mVpnService = binder.getService(); +			mBound = true; + +			if (mReceiver != null && mPending != null) { +				 +				boolean running = mVpnService.isRunning(); +				int resultCode = Activity.RESULT_CANCELED; +				 +				if (mPending.equals(ACTION_IS_EIP_RUNNING)) +					resultCode = (running) ? Activity.RESULT_OK : Activity.RESULT_CANCELED; +				if (mPending.equals(ACTION_START_EIP)) +					resultCode = (running) ? Activity.RESULT_OK : Activity.RESULT_CANCELED; +				else if (mPending.equals(ACTION_STOP_EIP)) +					resultCode = (running) ? Activity.RESULT_CANCELED +							: Activity.RESULT_OK; +				Bundle resultData = new Bundle(); +				resultData.putString(ConfigHelper.REQUEST_TAG, EIP_NOTIFICATION); +				mReceiver.send(resultCode, resultData); +				 +				mPending = null; +			} +		} + +		@Override +		public void onServiceDisconnected(ComponentName name) { +			// XXX tell mReceiver!! +			mBound = false; +			 +			if (mReceiver != null){ +				Bundle resultData = new Bundle(); +				resultData.putString(ConfigHelper.REQUEST_TAG, EIP_NOTIFICATION); +				mReceiver.send(Activity.RESULT_CANCELED, resultData); +			} +		} +	 +	}; +	 +	private void isRunning() { +		// TODO I don't like that whatever requested this never receives a response +		//		if OpenVpnService has not been START_SERVICE, though the one place this is used that's okay +		if (mBound) { +			if (mReceiver != null){ +				Bundle resultData = new Bundle(); +				resultData.putString(ConfigHelper.REQUEST_TAG, ACTION_IS_EIP_RUNNING); +				int resultCode = (mVpnService.isRunning()) ? Activity.RESULT_OK : Activity.RESULT_CANCELED; +				mReceiver.send(resultCode, resultData); +			} +		} else { +			mPending = ACTION_IS_EIP_RUNNING; +			this.retreiveVpnService(); +		} +	}  	private void startEIP() {  		if (activeGateway==null) @@ -80,11 +177,50 @@ public final class EIP extends IntentService {  		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  		intent.putExtra(LaunchVPN.EXTRA_KEY, activeGateway.mVpnProfile.getUUID().toString() );  		intent.putExtra(LaunchVPN.EXTRA_NAME, activeGateway.mVpnProfile.getName() ); +		intent.putExtra(ConfigHelper.RECEIVER_TAG, mReceiver);  		startActivity(intent); +		// Let's give it 2s to get rolling... TODO there really should be a better way to do this, or it's not needed. +		// Read more code in .openvpn package +		try { +			Thread.sleep(1000); +		} catch (InterruptedException e) { +			// TODO Auto-generated catch block +			e.printStackTrace(); +		} +		// Bind OpenVpnService for comm ops +		if (!mBound){ +			mPending = ACTION_START_EIP; +			this.retreiveVpnService(); +		} else { +			if (mReceiver != null) { +				Bundle resultData = new Bundle(); +				resultData.putString(ConfigHelper.REQUEST_TAG, ACTION_START_EIP); +				mReceiver.send(Activity.RESULT_OK, resultData); +			} +		}  	}  	private void stopEIP() { -		OpenVpnManagementThread.stopOpenVPN(); +		if (mBound){ +			mVpnService.onRevoke(); + +			/*if (mReceiver != null){ +				Bundle resultData = new Bundle(); +				resultData.putString(ConfigHelper.REQUEST_TAG, ACTION_STOP_EIP); +				mReceiver.send(Activity.RESULT_OK, resultData); +			}*/ +		} else { +			// TODO If OpenVpnService isn't bound, does that really always mean it's not running? +			//		If it's not running, bindService doesn't work w/o START_SERVICE action, so... +			/*mPending = ACTION_STOP_EIP; +			this.retreiveVpnService();*/ +		} +		// Remove this if above comes back +		if (mReceiver != null){ +			Bundle resultData = new Bundle(); +			resultData.putString(ConfigHelper.REQUEST_TAG, ACTION_STOP_EIP); +			mReceiver.send(Activity.RESULT_OK, resultData); +		}  	}  	private void updateEIPService() { diff --git a/src/se/leap/openvpn/LaunchVPN.java b/src/se/leap/openvpn/LaunchVPN.java index 2dcaf17..1df6be9 100644 --- a/src/se/leap/openvpn/LaunchVPN.java +++ b/src/se/leap/openvpn/LaunchVPN.java @@ -19,6 +19,9 @@ package se.leap.openvpn;  import java.io.IOException;  import java.util.Collection;  import java.util.Vector; + +import se.leap.leapclient.ConfigHelper; +import se.leap.leapclient.EIP;  import se.leap.leapclient.R;  import android.app.Activity; @@ -32,6 +35,7 @@ import android.content.SharedPreferences;  import android.net.VpnService;  import android.os.Bundle;  import android.os.Parcelable; +import android.os.ResultReceiver;  import android.preference.PreferenceManager;  import android.text.InputType;  import android.text.method.PasswordTransformationMethod; @@ -76,6 +80,8 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener {  	public static final int START_VPN_PROFILE= 70; +	// Dashboard, maybe more, want to know! +	private ResultReceiver mReceiver;  	private ProfileManager mPM;  	private VpnProfile mSelectedProfile; @@ -99,6 +105,9 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener {  		final Intent intent = getIntent();  		final String action = intent.getAction(); +		// If something wants feedback, they sent us a Receiver +		mReceiver = intent.getParcelableExtra(ConfigHelper.RECEIVER_TAG); +		  		// If the intent is a request to create a shortcut, we'll do that and exit @@ -273,7 +282,11 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener {  					new startOpenVpnThread().start();  				}  			} else if (resultCode == Activity.RESULT_CANCELED) { -				// User does not want us to start, so we just vanish +				// User does not want us to start, so we just vanish (well, now we tell our receiver, then vanish) +				Bundle resultData = new Bundle(); +				// For now, nothing else is calling, so this "request" string is good enough +				resultData.putString(ConfigHelper.REQUEST_TAG, EIP.ACTION_START_EIP); +				mReceiver.send(RESULT_CANCELED, resultData);  				finish();  			}  		} @@ -357,6 +370,11 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener {  		@Override  		public void run() {  			VPNLaunchHelper.startOpenVpn(mSelectedProfile, getBaseContext()); +			// Tell whom-it-may-concern that we started VPN +			Bundle resultData = new Bundle(); +			// For now, nothing else is calling, so this "request" string is good enough +			resultData.putString(ConfigHelper.REQUEST_TAG, EIP.ACTION_START_EIP); +			mReceiver.send(RESULT_OK, resultData);  			finish();  		} diff --git a/src/se/leap/openvpn/OpenVpnService.java b/src/se/leap/openvpn/OpenVpnService.java index 42c1de8..2408483 100644 --- a/src/se/leap/openvpn/OpenVpnService.java +++ b/src/se/leap/openvpn/OpenVpnService.java @@ -20,6 +20,8 @@ import java.io.IOException;  import java.lang.reflect.InvocationTargetException;  import java.lang.reflect.Method;  import java.util.Vector; + +import se.leap.leapclient.Dashboard;  import se.leap.leapclient.R;  import android.annotation.TargetApi; @@ -44,6 +46,7 @@ import se.leap.openvpn.OpenVPN.StateListener;  public class OpenVpnService extends VpnService implements StateListener, Callback {  	public static final String START_SERVICE = "se.leap.openvpn.START_SERVICE"; +	public static final String RETRIEVE_SERVICE = "se.leap.openvpn.RETRIEVE_SERVICE";  	private Thread mProcessThread=null; @@ -88,7 +91,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac  	@Override  	public IBinder onBind(Intent intent) {  		String action = intent.getAction(); -		if( action !=null && action.equals(START_SERVICE)) +		if( action !=null && (action.equals(START_SERVICE) || action.equals(RETRIEVE_SERVICE)) )  			return mBinder;  		else  			return super.onBind(intent); @@ -222,7 +225,8 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac  	@Override  	public int onStartCommand(Intent intent, int flags, int startId) {		 -		if(intent != null && intent.getAction() !=null &&intent.getAction().equals(START_SERVICE)) +		if( intent != null && intent.getAction() !=null && +				(intent.getAction().equals(START_SERVICE) || intent.getAction().equals(RETRIEVE_SERVICE)) )  			return START_NOT_STICKY; @@ -465,6 +469,13 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac  		mLocalIPv6 = ipv6addr;  	} +	public boolean isRunning() { +		if (mStarting == true || mProcessThread != null) +			return true; +		else +			return false; +	} +	  	@Override  	public void updateState(String state,String logmessage, int resid) {  		// If the process is not running, ignore any state,  | 
