summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Leonard <meanderingcode@aetherislands.net>2013-06-09 04:31:27 -0600
committerSean Leonard <meanderingcode@aetherislands.net>2013-06-20 18:46:39 -0600
commit389dfcdfad555feb1cf212ef9b42626633d5eade (patch)
tree4485c33699424bbb20f94e610171fa1ef01b08e2
parent33338d43e0e83329a7c46807e096b8148e19aff7 (diff)
Better control and UI feedback for VPN
-rw-r--r--src/se/leap/leapclient/ConfigHelper.java4
-rw-r--r--src/se/leap/leapclient/Dashboard.java138
-rw-r--r--src/se/leap/leapclient/EIP.java140
-rw-r--r--src/se/leap/openvpn/LaunchVPN.java20
-rw-r--r--src/se/leap/openvpn/OpenVpnService.java15
5 files changed, 308 insertions, 9 deletions
diff --git a/src/se/leap/leapclient/ConfigHelper.java b/src/se/leap/leapclient/ConfigHelper.java
index 1c2a482a..cab5fdee 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 31719d90..f10966b2 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 21cbdfd5..867805bd 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 2dcaf176..1df6be96 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 42c1de8a..2408483d 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,