summaryrefslogtreecommitdiff
path: root/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/eip/EIP.java')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java563
1 files changed, 400 insertions, 163 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
index 665e0ebd..92bd5929 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -16,25 +16,46 @@
*/
package se.leap.bitmaskclient.eip;
-import android.app.IntentService;
+import android.annotation.SuppressLint;
+import android.app.Service;
+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.RemoteException;
import android.os.ResultReceiver;
+import android.os.SystemClock;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.support.annotation.WorkerThread;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
+import java.io.Closeable;
import java.lang.ref.WeakReference;
+import java.util.Observable;
+import java.util.Observer;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
import de.blinkt.openvpn.LaunchVPN;
+import de.blinkt.openvpn.core.IOpenVPNServiceInternal;
+import de.blinkt.openvpn.core.OpenVPNService;
+import de.blinkt.openvpn.core.ProfileManager;
+import de.blinkt.openvpn.core.VpnStatus;
import se.leap.bitmaskclient.OnBootReceiver;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
import static android.content.Intent.CATEGORY_DEFAULT;
+import static se.leap.bitmaskclient.ConfigHelper.ensureNotOnMainThread;
import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT;
import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE;
import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;
@@ -43,11 +64,11 @@ import static se.leap.bitmaskclient.Constants.EIP_ACTION_IS_RUNNING;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_START;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_ALWAYS_ON_VPN;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP;
+import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP_BLOCKING_VPN;
import static se.leap.bitmaskclient.Constants.EIP_EARLY_ROUTES;
import static se.leap.bitmaskclient.Constants.EIP_RECEIVER;
import static se.leap.bitmaskclient.Constants.EIP_REQUEST;
import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT;
-import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION;
import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
import static se.leap.bitmaskclient.MainActivityErrorDialog.DOWNLOAD_ERRORS.ERROR_INVALID_VPN_CERTIFICATE;
@@ -56,227 +77,443 @@ import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid;
/**
* EIP is the abstract base class for interacting with and managing the Encrypted
* Internet Proxy connection. Connections are started, stopped, and queried through
- * this IntentService.
+ * this Service.
* Contains logic for parsing eip-service.json from the provider, configuring and selecting
* gateways, and controlling {@link de.blinkt.openvpn.core.OpenVPNService} connections.
*
* @author Sean Leonard <meanderingcode@aetherislands.net>
* @author Parménides GV <parmegv@sdf.org>
*/
-public final class EIP extends IntentService {
+public final class EIP extends Service implements Observer {
public final static String TAG = EIP.class.getSimpleName(),
SERVICE_API_PATH = "config/eip-service.json",
ERRORS = "errors",
ERROR_ID = "errorID";
- private WeakReference<ResultReceiver> mReceiverRef = new WeakReference<>(null);
- private SharedPreferences preferences;
+ private volatile SharedPreferences preferences;
+ private AtomicInteger processCounter;
+ private volatile EipStatus eipStatus;
+ private final Object lock = new Object();
- public EIP() {
- super(TAG);
+ // Service connection to OpenVpnService, shared between threads
+ private volatile OpenvpnServiceConnection openvpnServiceConnection;
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
}
@Override
public void onCreate() {
super.onCreate();
+ eipStatus = EipStatus.getInstance();
+ eipStatus.addObserver(this);
+ processCounter = new AtomicInteger(0);
preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
}
@Override
- protected void onHandleIntent(Intent intent) {
- String action = intent.getAction();
- if (intent.getParcelableExtra(EIP_RECEIVER) != null) {
- mReceiverRef = new WeakReference<>((ResultReceiver) intent.getParcelableExtra(EIP_RECEIVER));
+ public void onDestroy() {
+ super.onDestroy();
+ eipStatus.deleteObserver(this);
+ if (openvpnServiceConnection != null) {
+ openvpnServiceConnection.close();
+ openvpnServiceConnection = null;
}
+ }
- if (action == null) {
- return;
+ /**
+ * update eipStatus whenever it changes
+ */
+ @Override
+ public void update(Observable observable, Object data) {
+ if (observable instanceof EipStatus) {
+ eipStatus = (EipStatus) observable;
}
+ }
- switch (action) {
- case EIP_ACTION_START:
- boolean earlyRoutes = intent.getBooleanExtra(EIP_EARLY_ROUTES, true);
- startEIP(earlyRoutes);
- break;
- case EIP_ACTION_START_ALWAYS_ON_VPN:
- startEIPAlwaysOnVpn();
- break;
- case EIP_ACTION_STOP:
- stopEIP();
- break;
- case EIP_ACTION_IS_RUNNING:
- isRunning();
- break;
- case EIP_ACTION_CHECK_CERT_VALIDITY:
- checkVPNCertificateValidity();
- break;
+ /**
+ * register new process with processCounter and start it
+ * @param intent the intent that started this EIP call
+ */
+ @Override
+ public int onStartCommand(final Intent intent, int flags, int startId) {
+ Log.d(TAG, "new process counter: " + processCounter.incrementAndGet());
+ final String action = intent.getAction();
+ if (action != null) {
+ ResultReceiver resultReceiver = intent.getParcelableExtra(EIP_RECEIVER);
+ new EipThread(this, action, intent, resultReceiver).start();
+ } else {
+ processCounter.decrementAndGet();
}
+ return START_STICKY;
}
/**
- * Initiates an EIP connection by selecting a gateway and preparing and sending an
- * Intent to {@link de.blinkt.openvpn.LaunchVPN}.
- * It also sets up early routes.
+ * Thread to handle the requests to this service
*/
- private void startEIP(boolean earlyRoutes) {
- if (!EipStatus.getInstance().isBlockingVpnEstablished() && earlyRoutes) {
- earlyRoutes();
+ class EipThread extends Thread {
+ EipThread(EIP eipReference, String action, Intent startIntent, ResultReceiver resultReceiver) {
+ super(new EipRunnable(eipReference, action, startIntent, resultReceiver));
}
+ }
+
+ /**
+ * Runnable to be used in the EipThread class
+ */
+ class EipRunnable implements Runnable {
+ private String action;
+ private EIP eipReference;
+ private Intent intent;
- Bundle result = new Bundle();
+ private WeakReference<ResultReceiver> mReceiverRef = new WeakReference<>(null);
- if (!preferences.getBoolean(EIP_RESTART_ON_BOOT, false)){
- preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).commit();
+
+ EipRunnable(EIP eipReference, String action, Intent startIntent, ResultReceiver resultReceiver) {
+ super();
+ this.action = action;
+ this.eipReference = eipReference;
+ this.intent = startIntent;
+ this.mReceiverRef = new WeakReference<ResultReceiver>(resultReceiver);
}
- GatewaysManager gatewaysManager = gatewaysFromPreferences();
- if (!isVPNCertificateValid()){
- setErrorResult(result, vpn_certificate_is_invalid, ERROR_INVALID_VPN_CERTIFICATE.toString());
- tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED, result);
- return;
+ /**
+ * select correct function to call based upon transmitted action
+ * after completing check if no other thread is running
+ * if no other thread is running terminate the EIP service
+ */
+ @Override
+ public void run() {
+ Log.d(TAG, "new EIP thread started!");
+ switch (action) {
+ case EIP_ACTION_START:
+ boolean earlyRoutes = intent.getBooleanExtra(EIP_EARLY_ROUTES, true);
+ startEIP(earlyRoutes);
+ break;
+ case EIP_ACTION_START_ALWAYS_ON_VPN:
+ startEIPAlwaysOnVpn();
+ break;
+ case EIP_ACTION_STOP:
+ stopEIP();
+ break;
+ case EIP_ACTION_IS_RUNNING:
+ isRunning();
+ break;
+ case EIP_ACTION_CHECK_CERT_VALIDITY:
+ checkVPNCertificateValidity();
+ break;
+ }
+ if (processCounter.decrementAndGet() == 0) {
+ Log.d(TAG, "no more running EIP threads!");
+ eipReference.stopSelf();
+ }
}
- Gateway gateway = gatewaysManager.select();
- if (gateway != null && gateway.getProfile() != null) {
- launchActiveGateway(gateway);
- tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_OK);
- } else
- tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED);
- }
+ /**
+ * Initiates an EIP connection by selecting a gateway and preparing and sending an
+ * Intent to {@link de.blinkt.openvpn.LaunchVPN}.
+ * It also sets up early routes.
+ */
+ @SuppressLint("ApplySharedPref")
+ private void startEIP(boolean earlyRoutes) {
+ if (!eipStatus.isBlockingVpnEstablished() && earlyRoutes) {
+ earlyRoutes();
+ }
- /**
- * Tries to start the last used vpn profile when the OS was rebooted and always-on-VPN is enabled.
- * The {@link OnBootReceiver} will care if there is no profile.
- */
- private void startEIPAlwaysOnVpn() {
- Log.d(TAG, "startEIPAlwaysOnVpn vpn");
+ Bundle result = new Bundle();
+ if (!preferences.getBoolean(EIP_RESTART_ON_BOOT, false)) {
+ preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).commit();
+ }
- GatewaysManager gatewaysManager = gatewaysFromPreferences();
- Gateway gateway = gatewaysManager.select();
+ GatewaysManager gatewaysManager = gatewaysFromPreferences();
+ if (!isVPNCertificateValid()) {
+ setErrorResult(result, vpn_certificate_is_invalid, ERROR_INVALID_VPN_CERTIFICATE.toString());
+ tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED, result);
+ return;
+ }
- if (gateway != null && gateway.getProfile() != null) {
- Log.d(TAG, "startEIPAlwaysOnVpn eip launch avtive gateway vpn");
- launchActiveGateway(gateway);
- } else {
- Log.d(TAG, "startEIPAlwaysOnVpn no active profile available!");
+ Gateway gateway = gatewaysManager.select();
+ if (gateway != null && gateway.getProfile() != null) {
+ launchActiveGateway(gateway);
+ tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_OK);
+ } else
+ tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED);
}
- }
- /**
- * Early routes are routes that block traffic until a new
- * VpnService is started properly.
- */
- private void earlyRoutes() {
- Intent voidVpnLauncher = new Intent(getApplicationContext(), VoidVpnLauncher.class);
- voidVpnLauncher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(voidVpnLauncher);
- }
+ /**
+ * Tries to start the last used vpn profile when the OS was rebooted and always-on-VPN is enabled.
+ * The {@link OnBootReceiver} will care if there is no profile.
+ */
+ private void startEIPAlwaysOnVpn() {
+ Log.d(TAG, "startEIPAlwaysOnVpn vpn");
+
+ GatewaysManager gatewaysManager = gatewaysFromPreferences();
+ Gateway gateway = gatewaysManager.select();
+
+ if (gateway != null && gateway.getProfile() != null) {
+ Log.d(TAG, "startEIPAlwaysOnVpn eip launch avtive gateway vpn");
+ launchActiveGateway(gateway);
+ } else {
+ Log.d(TAG, "startEIPAlwaysOnVpn no active profile available!");
+ }
+ }
- private void launchActiveGateway(Gateway gateway) {
- Intent intent = new Intent(this, LaunchVPN.class);
- intent.setAction(Intent.ACTION_MAIN);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true);
- intent.putExtra(LaunchVPN.EXTRA_TEMP_VPN_PROFILE, gateway.getProfile());
- startActivity(intent);
- }
+ /**
+ * Early routes are routes that block traffic until a new
+ * VpnService is started properly.
+ */
+ private void earlyRoutes() {
+ Intent voidVpnLauncher = new Intent(getApplicationContext(), VoidVpnLauncher.class);
+ voidVpnLauncher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(voidVpnLauncher);
+ }
- private void stopEIP() {
- // TODO stop eip from here if possible...
- // TODO then refactor EipFragment.handleSwitchOff
- EipStatus eipStatus = EipStatus.getInstance();
- int resultCode = RESULT_CANCELED;
- if (eipStatus.isConnected() || eipStatus.isConnecting())
- resultCode = RESULT_OK;
+ /**
+ * starts the VPN and connects to the given gateway
+ *
+ * @param gateway to connect to
+ */
+ private void launchActiveGateway(Gateway gateway) {
+ Intent intent = new Intent(eipReference, LaunchVPN.class);
+ intent.setAction(Intent.ACTION_MAIN);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true);
+ intent.putExtra(LaunchVPN.EXTRA_TEMP_VPN_PROFILE, gateway.getProfile());
+ startActivity(intent);
+ }
- tellToReceiverOrBroadcast(EIP_ACTION_STOP, resultCode);
- }
+ /**
+ * Stop VPN
+ * First checks if the OpenVpnConnection is open then
+ * terminates EIP if currently connected or connecting
+ */
+ private void stopEIP() {
+ int resultCode = stop() ? RESULT_OK : RESULT_CANCELED;
+ tellToReceiverOrBroadcast(EIP_ACTION_STOP, resultCode);
+ }
- /**
- * Checks the last stored status notified by ics-openvpn
- * Sends <code>Activity.RESULT_CANCELED</code> to the ResultReceiver that made the
- * request if it's not connected, <code>Activity.RESULT_OK</code> otherwise.
- */
- private void isRunning() {
- EipStatus eipStatus = EipStatus.getInstance();
- int resultCode = (eipStatus.isConnected()) ?
- RESULT_OK :
- RESULT_CANCELED;
- tellToReceiverOrBroadcast(EIP_ACTION_IS_RUNNING, resultCode);
- }
+ /**
+ * Checks the last stored status notified by ics-openvpn
+ * Sends <code>Activity.RESULT_CANCELED</code> to the ResultReceiver that made the
+ * request if it's not connected, <code>Activity.RESULT_OK</code> otherwise.
+ */
+ private void isRunning() {
+ int resultCode = (eipStatus.isConnected()) ?
+ RESULT_OK :
+ RESULT_CANCELED;
+ tellToReceiverOrBroadcast(EIP_ACTION_IS_RUNNING, resultCode);
+ }
- private JSONObject eipDefinitionFromPreferences() {
- JSONObject result = new JSONObject();
- try {
- String eipDefinitionString = preferences.getString(PROVIDER_EIP_DEFINITION, "");
- if (!eipDefinitionString.isEmpty()) {
- result = new JSONObject(eipDefinitionString);
+ /**
+ * read eipServiceJson from preferences and parse Gateways
+ *
+ * @return GatewaysManager
+ */
+ private GatewaysManager gatewaysFromPreferences() {
+ GatewaysManager gatewaysManager = new GatewaysManager(eipReference, preferences);
+ gatewaysManager.configureFromPreferences();
+ return gatewaysManager;
+ }
+
+ /**
+ * read VPN certificate from preferences and check it
+ * broadcast result
+ */
+ private void checkVPNCertificateValidity() {
+ int resultCode = isVPNCertificateValid() ?
+ RESULT_OK :
+ RESULT_CANCELED;
+ tellToReceiverOrBroadcast(EIP_ACTION_CHECK_CERT_VALIDITY, resultCode);
+ }
+
+ /**
+ * read VPN certificate from preferences and check it
+ *
+ * @return true if VPN certificate is valid false otherwise
+ */
+ private boolean isVPNCertificateValid() {
+ VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(PROVIDER_VPN_CERTIFICATE, ""));
+ return validator.isValid();
+ }
+
+ /**
+ * send resultCode and resultData to receiver or
+ * broadcast the result if no receiver is defined
+ *
+ * @param action the action that has been performed
+ * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise
+ * @param resultData other data to broadcast or return to receiver
+ */
+ private void tellToReceiverOrBroadcast(String action, int resultCode, Bundle resultData) {
+ resultData.putString(EIP_REQUEST, action);
+ if (mReceiverRef.get() != null) {
+ mReceiverRef.get().send(resultCode, resultData);
+ } else {
+ broadcastEvent(resultCode, resultData);
}
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
}
- return result;
- }
- private GatewaysManager gatewaysFromPreferences() {
- GatewaysManager gatewaysManager = new GatewaysManager(this, preferences);
- //TODO: THIS IS A QUICK FIX - it deletes all profiles in ProfileManager, thus it's possible
- // to add all gateways from prefs without duplicates, but this should be refactored.
- gatewaysManager.clearGatewaysAndProfiles();
- gatewaysManager.fromEipServiceJson(eipDefinitionFromPreferences());
- return gatewaysManager;
- }
+ /**
+ * send resultCode and resultData to receiver or
+ * broadcast the result if no receiver is defined
+ *
+ * @param action the action that has been performed
+ * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise
+ */
+ private void tellToReceiverOrBroadcast(String action, int resultCode) {
+ tellToReceiverOrBroadcast(action, resultCode, new Bundle());
+ }
- private void checkVPNCertificateValidity() {
- int resultCode = isVPNCertificateValid() ?
- RESULT_OK :
- RESULT_CANCELED;
- tellToReceiverOrBroadcast(EIP_ACTION_CHECK_CERT_VALIDITY, resultCode);
- }
+ /**
+ * broadcast result
+ *
+ * @param resultCode RESULT_OK if action was successful RESULT_CANCELED otherwise
+ * @param resultData other data to broadcast or return to receiver
+ */
+ private void broadcastEvent(int resultCode, Bundle resultData) {
+ Intent intentUpdate = new Intent(BROADCAST_EIP_EVENT);
+ intentUpdate.addCategory(CATEGORY_DEFAULT);
+ intentUpdate.putExtra(BROADCAST_RESULT_CODE, resultCode);
+ intentUpdate.putExtra(BROADCAST_RESULT_KEY, resultData);
+ Log.d(TAG, "sending broadcast");
+ LocalBroadcastManager.getInstance(eipReference).sendBroadcast(intentUpdate);
+ }
- private boolean isVPNCertificateValid() {
- VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(PROVIDER_VPN_CERTIFICATE, ""));
- return validator.isValid();
- }
- private void tellToReceiverOrBroadcast(String action, int resultCode, Bundle resultData) {
- resultData.putString(EIP_REQUEST, action);
- if (mReceiverRef.get() != null) {
- mReceiverRef.get().send(resultCode, resultData);
- } else {
- broadcastEvent(resultCode, resultData);
+ /**
+ * helper function to add error to result bundle
+ *
+ * @param result - result of an action
+ * @param errorMessageId - id of string resource describing the error
+ * @param errorId - MainActivityErrorDialog DownloadError id
+ */
+ void setErrorResult(Bundle result, @StringRes int errorMessageId, String errorId) {
+ JSONObject errorJson = new JSONObject();
+ try {
+ errorJson.put(ERRORS, getResources().getString(errorMessageId));
+ errorJson.put(ERROR_ID, errorId);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ result.putString(ERRORS, errorJson.toString());
+ result.putBoolean(BROADCAST_RESULT_KEY, false);
}
- }
- private void tellToReceiverOrBroadcast(String action, int resultCode) {
- tellToReceiverOrBroadcast(action, resultCode, new Bundle());
- }
- private void broadcastEvent(int resultCode , Bundle resultData) {
- Intent intentUpdate = new Intent(BROADCAST_EIP_EVENT);
- intentUpdate.addCategory(CATEGORY_DEFAULT);
- intentUpdate.putExtra(BROADCAST_RESULT_CODE, resultCode);
- intentUpdate.putExtra(BROADCAST_RESULT_KEY, resultData);
- Log.d(TAG, "sending broadcast");
- LocalBroadcastManager.getInstance(this).sendBroadcast(intentUpdate);
- }
+ /**
+ * disable Bitmask starting on after phone reboot
+ * then stop VPN
+ */
+ private boolean stop() {
+ preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, false).apply();
+ if (eipStatus.isBlockingVpnEstablished()) {
+ stopBlockingVpn();
+ }
+ return disconnect();
+ }
- Bundle setErrorResult(Bundle result, int errorMessageId, String errorId) {
- JSONObject errorJson = new JSONObject();
- addErrorMessageToJson(errorJson, getResources().getString(errorMessageId), errorId);
- result.putString(ERRORS, errorJson.toString());
- result.putBoolean(BROADCAST_RESULT_KEY, false);
- return result;
+ /**
+ * stop void vpn from blocking internet
+ */
+ private void stopBlockingVpn() {
+ Log.d(TAG, "stop VoidVpn!");
+ Intent stopVoidVpnIntent = new Intent(eipReference, VoidVpnService.class);
+ stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN);
+ startService(stopVoidVpnIntent);
+ }
+
+
+ /**
+ * creates a OpenVpnServiceConnection if necessary
+ * then terminates OpenVPN
+ */
+ private boolean disconnect() {
+ try {
+ initOpenVpnServiceConnection();
+ } catch (InterruptedException | IllegalStateException e) {
+ return false;
+ }
+
+ ProfileManager.setConntectedVpnProfileDisconnected(eipReference);
+ try {
+ return openvpnServiceConnection.getService().stopVPN(false);
+ } catch (RemoteException e) {
+ VpnStatus.logException(e);
+ }
+ return false;
+ }
+
+ /**
+ * Assigns a new OpenvpnServiceConnection to EIP's member variable openvpnServiceConnection.
+ * Only one thread at a time can create the service connection, that will be shared between threads
+ *
+ * @throws InterruptedException thrown if thread gets interrupted
+ * @throws IllegalStateException thrown if this method was not called from a background thread
+ */
+ private void initOpenVpnServiceConnection() throws InterruptedException, IllegalStateException {
+ synchronized (lock) {
+ if (openvpnServiceConnection == null) {
+ Log.d(TAG, "serviceConnection is still null");
+ openvpnServiceConnection = new OpenvpnServiceConnection(eipReference);
+ }
+ }
+ }
}
- private void addErrorMessageToJson(JSONObject jsonObject, String errorMessage, String errorId) {
- try {
- jsonObject.put(ERRORS, errorMessage);
- jsonObject.put(ERROR_ID, errorId);
- } catch (JSONException e) {
- e.printStackTrace();
+ /**
+ * Creates a service connection to OpenVpnService.
+ * The constructor blocks until the service is bound to the given Context.
+ * Pattern stolen from android.security.KeyChain.java
+ */
+ @WorkerThread
+ public static class OpenvpnServiceConnection implements Closeable {
+ private final Context context;
+ private ServiceConnection serviceConnection;
+ private IOpenVPNServiceInternal service;
+
+ protected OpenvpnServiceConnection(Context context) throws InterruptedException, IllegalStateException {
+ this.context = context;
+ ensureNotOnMainThread(context);
+ Log.d(TAG, "initSynchronizedServiceConnection!");
+ initSynchronizedServiceConnection(context);
+ }
+
+ private void initSynchronizedServiceConnection(final Context context) throws InterruptedException {
+ final BlockingQueue<IOpenVPNServiceInternal> blockingQueue = new LinkedBlockingQueue<>(1);
+ this.serviceConnection = new ServiceConnection() {
+ volatile boolean mConnectedAtLeastOnce = false;
+ @Override public void onServiceConnected(ComponentName name, IBinder service) {
+ if (!mConnectedAtLeastOnce) {
+ mConnectedAtLeastOnce = true;
+ try {
+ Log.d(TAG, "serviceConnection connected! ProcessCounter: " + ((EIP) context).processCounter.get());
+ blockingQueue.put(IOpenVPNServiceInternal.Stub.asInterface(service));
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ @Override public void onServiceDisconnected(ComponentName name) {
+ Log.d(TAG, "serviceConnection disconnected! ProcessCounter: " + ((EIP) context).processCounter.get());
+ }
+ };
+
+ Intent intent = new Intent(context, OpenVPNService.class);
+ intent.setAction(OpenVPNService.START_SERVICE);
+ context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+ service = blockingQueue.take();
+ }
+
+ @Override public void close() {
+ context.unbindService(serviceConnection);
+ }
+
+ public IOpenVPNServiceInternal getService() {
+ return service;
}
}
+
}
+